xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/MachO.cpp (revision 7937fe1a17f2c883f41e1bdefd0a1d9c93861532)
13e1d4ec6SLang Hames //===----------------- MachO.cpp - MachO format utilities -----------------===//
23e1d4ec6SLang Hames //
33e1d4ec6SLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43e1d4ec6SLang Hames // See https://llvm.org/LICENSE.txt for license information.
53e1d4ec6SLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63e1d4ec6SLang Hames //
73e1d4ec6SLang Hames //===----------------------------------------------------------------------===//
83e1d4ec6SLang Hames 
93e1d4ec6SLang Hames #include "llvm/ExecutionEngine/Orc/MachO.h"
103e1d4ec6SLang Hames 
117a4013f0SLang Hames #include "llvm/ADT/ScopeExit.h"
123e1d4ec6SLang Hames #include "llvm/BinaryFormat/MachO.h"
13*7937fe1aSLang Hames #include "llvm/ExecutionEngine/Orc/Layer.h"
143e1d4ec6SLang Hames #include "llvm/Object/MachOUniversal.h"
15e15abb79SLang Hames #include "llvm/Support/FileSystem.h"
163e1d4ec6SLang Hames 
173e1d4ec6SLang Hames #define DEBUG_TYPE "orc"
183e1d4ec6SLang Hames 
193e1d4ec6SLang Hames namespace llvm {
203e1d4ec6SLang Hames namespace orc {
213e1d4ec6SLang Hames 
224a127221SLang Hames static std::string objDesc(const MemoryBufferRef &Obj, const Triple &TT,
233e1d4ec6SLang Hames                            bool ObjIsSlice) {
243e1d4ec6SLang Hames   std::string Desc;
253e1d4ec6SLang Hames   if (ObjIsSlice)
263e1d4ec6SLang Hames     Desc += (TT.getArchName() + " slice of universal binary").str();
273e1d4ec6SLang Hames   Desc += Obj.getBufferIdentifier();
283e1d4ec6SLang Hames   return Desc;
293e1d4ec6SLang Hames }
303e1d4ec6SLang Hames 
313e1d4ec6SLang Hames template <typename HeaderType>
324a127221SLang Hames static Error checkMachORelocatableObject(MemoryBufferRef Obj,
333e1d4ec6SLang Hames                                          bool SwapEndianness, const Triple &TT,
343e1d4ec6SLang Hames                                          bool ObjIsSlice) {
354a127221SLang Hames   StringRef Data = Obj.getBuffer();
363e1d4ec6SLang Hames 
373e1d4ec6SLang Hames   HeaderType Hdr;
383e1d4ec6SLang Hames   memcpy(&Hdr, Data.data(), sizeof(HeaderType));
393e1d4ec6SLang Hames 
403e1d4ec6SLang Hames   if (SwapEndianness)
413e1d4ec6SLang Hames     swapStruct(Hdr);
423e1d4ec6SLang Hames 
433e1d4ec6SLang Hames   if (Hdr.filetype != MachO::MH_OBJECT)
444a127221SLang Hames     return make_error<StringError>(objDesc(Obj, TT, ObjIsSlice) +
453e1d4ec6SLang Hames                                        " is not a MachO relocatable object",
463e1d4ec6SLang Hames                                    inconvertibleErrorCode());
473e1d4ec6SLang Hames 
483e1d4ec6SLang Hames   auto ObjArch = object::MachOObjectFile::getArch(Hdr.cputype, Hdr.cpusubtype);
493e1d4ec6SLang Hames   if (ObjArch != TT.getArch())
503e1d4ec6SLang Hames     return make_error<StringError>(
514a127221SLang Hames         objDesc(Obj, TT, ObjIsSlice) + Triple::getArchTypeName(ObjArch) +
523e1d4ec6SLang Hames             ", cannot be loaded into " + TT.str() + " process",
533e1d4ec6SLang Hames         inconvertibleErrorCode());
543e1d4ec6SLang Hames 
554a127221SLang Hames   return Error::success();
563e1d4ec6SLang Hames }
573e1d4ec6SLang Hames 
584a127221SLang Hames Error checkMachORelocatableObject(MemoryBufferRef Obj, const Triple &TT,
593e1d4ec6SLang Hames                                   bool ObjIsSlice) {
604a127221SLang Hames   StringRef Data = Obj.getBuffer();
613e1d4ec6SLang Hames 
623e1d4ec6SLang Hames   if (Data.size() < 4)
633e1d4ec6SLang Hames     return make_error<StringError>(
644a127221SLang Hames         objDesc(Obj, TT, ObjIsSlice) +
653e1d4ec6SLang Hames             " is not a valid MachO relocatable object file (truncated header)",
663e1d4ec6SLang Hames         inconvertibleErrorCode());
673e1d4ec6SLang Hames 
683e1d4ec6SLang Hames   uint32_t Magic;
693e1d4ec6SLang Hames   memcpy(&Magic, Data.data(), sizeof(uint32_t));
703e1d4ec6SLang Hames 
713e1d4ec6SLang Hames   switch (Magic) {
723e1d4ec6SLang Hames   case MachO::MH_MAGIC:
733e1d4ec6SLang Hames   case MachO::MH_CIGAM:
743e1d4ec6SLang Hames     return checkMachORelocatableObject<MachO::mach_header>(
753e1d4ec6SLang Hames         std::move(Obj), Magic == MachO::MH_CIGAM, TT, ObjIsSlice);
763e1d4ec6SLang Hames   case MachO::MH_MAGIC_64:
773e1d4ec6SLang Hames   case MachO::MH_CIGAM_64:
783e1d4ec6SLang Hames     return checkMachORelocatableObject<MachO::mach_header_64>(
793e1d4ec6SLang Hames         std::move(Obj), Magic == MachO::MH_CIGAM_64, TT, ObjIsSlice);
803e1d4ec6SLang Hames   default:
813e1d4ec6SLang Hames     return make_error<StringError>(
824a127221SLang Hames         objDesc(Obj, TT, ObjIsSlice) +
833e1d4ec6SLang Hames             " is not a valid MachO relocatable object (bad magic value)",
843e1d4ec6SLang Hames         inconvertibleErrorCode());
853e1d4ec6SLang Hames   }
863e1d4ec6SLang Hames }
873e1d4ec6SLang Hames 
883e1d4ec6SLang Hames Expected<std::unique_ptr<MemoryBuffer>>
894a127221SLang Hames checkMachORelocatableObject(std::unique_ptr<MemoryBuffer> Obj, const Triple &TT,
904a127221SLang Hames                             bool ObjIsSlice) {
914a127221SLang Hames   if (auto Err =
924a127221SLang Hames           checkMachORelocatableObject(Obj->getMemBufferRef(), TT, ObjIsSlice))
934a127221SLang Hames     return std::move(Err);
944a127221SLang Hames   return std::move(Obj);
954a127221SLang Hames }
964a127221SLang Hames 
977a4013f0SLang Hames Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>
987a4013f0SLang Hames loadMachORelocatableObject(StringRef Path, const Triple &TT, LoadArchives LA,
99e15abb79SLang Hames                            std::optional<StringRef> IdentifierOverride) {
1003e1d4ec6SLang Hames   assert((TT.getObjectFormat() == Triple::UnknownObjectFormat ||
1013e1d4ec6SLang Hames           TT.getObjectFormat() == Triple::MachO) &&
1023e1d4ec6SLang Hames          "TT must specify MachO or Unknown object format");
1033e1d4ec6SLang Hames 
104e15abb79SLang Hames   if (!IdentifierOverride)
105e15abb79SLang Hames     IdentifierOverride = Path;
106e15abb79SLang Hames 
107e15abb79SLang Hames   Expected<sys::fs::file_t> FDOrErr =
108e15abb79SLang Hames       sys::fs::openNativeFileForRead(Path, sys::fs::OF_None);
109e15abb79SLang Hames   if (!FDOrErr)
110e15abb79SLang Hames     return createFileError(Path, FDOrErr.takeError());
111e15abb79SLang Hames   sys::fs::file_t FD = *FDOrErr;
1127a4013f0SLang Hames   auto CloseFile = make_scope_exit([&]() { sys::fs::closeFile(FD); });
1137a4013f0SLang Hames 
114e15abb79SLang Hames   auto Buf =
115e15abb79SLang Hames       MemoryBuffer::getOpenFile(FD, *IdentifierOverride, /*FileSize=*/-1);
1163e1d4ec6SLang Hames   if (!Buf)
117e15abb79SLang Hames     return make_error<StringError>(
118e15abb79SLang Hames         StringRef("Could not load MachO object at path ") + Path,
119e15abb79SLang Hames         Buf.getError());
1203e1d4ec6SLang Hames 
1213e1d4ec6SLang Hames   switch (identify_magic((*Buf)->getBuffer())) {
1227a4013f0SLang Hames   case file_magic::macho_object: {
1237a4013f0SLang Hames     auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, false);
1247a4013f0SLang Hames     if (!CheckedObj)
1257a4013f0SLang Hames       return CheckedObj.takeError();
1267a4013f0SLang Hames     return std::make_pair(std::move(*CheckedObj),
1277a4013f0SLang Hames                           LinkableFileKind::RelocatableObject);
1287a4013f0SLang Hames   }
1293e1d4ec6SLang Hames   case file_magic::macho_universal_binary:
1307a4013f0SLang Hames     return loadLinkableSliceFromMachOUniversalBinary(FD, std::move(*Buf), TT,
1317a4013f0SLang Hames                                                      LoadArchives::Never, Path,
1327a4013f0SLang Hames                                                      *IdentifierOverride);
1333e1d4ec6SLang Hames   default:
1343e1d4ec6SLang Hames     return make_error<StringError>(
1353e1d4ec6SLang Hames         Path + " does not contain a relocatable object file compatible with " +
1363e1d4ec6SLang Hames             TT.str(),
1373e1d4ec6SLang Hames         inconvertibleErrorCode());
1383e1d4ec6SLang Hames   }
1393e1d4ec6SLang Hames }
1403e1d4ec6SLang Hames 
1417a4013f0SLang Hames Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>
1427a4013f0SLang Hames loadLinkableSliceFromMachOUniversalBinary(sys::fs::file_t FD,
1437a4013f0SLang Hames                                           std::unique_ptr<MemoryBuffer> UBBuf,
1447a4013f0SLang Hames                                           const Triple &TT, LoadArchives LA,
1457a4013f0SLang Hames                                           StringRef UBPath,
1467a4013f0SLang Hames                                           StringRef Identifier) {
1473e1d4ec6SLang Hames 
1483e1d4ec6SLang Hames   auto UniversalBin =
1493e1d4ec6SLang Hames       object::MachOUniversalBinary::create(UBBuf->getMemBufferRef());
1503e1d4ec6SLang Hames   if (!UniversalBin)
1513e1d4ec6SLang Hames     return UniversalBin.takeError();
1523e1d4ec6SLang Hames 
1533e1d4ec6SLang Hames   auto SliceRange = getMachOSliceRangeForTriple(**UniversalBin, TT);
1543e1d4ec6SLang Hames   if (!SliceRange)
1553e1d4ec6SLang Hames     return SliceRange.takeError();
1563e1d4ec6SLang Hames 
1577a4013f0SLang Hames   auto Buf = MemoryBuffer::getOpenFileSlice(FD, Identifier, SliceRange->second,
1587a4013f0SLang Hames                                             SliceRange->first);
159e15abb79SLang Hames   if (!Buf)
160e15abb79SLang Hames     return make_error<StringError>(
161e15abb79SLang Hames         "Could not load " + TT.getArchName() +
162e15abb79SLang Hames             " slice of MachO universal binary at path " + UBPath,
163e15abb79SLang Hames         Buf.getError());
164e15abb79SLang Hames 
1657a4013f0SLang Hames   switch (identify_magic((*Buf)->getBuffer())) {
1667a4013f0SLang Hames   case file_magic::archive:
1677a4013f0SLang Hames     if (LA != LoadArchives::Never)
1687a4013f0SLang Hames       return std::make_pair(std::move(*Buf), LinkableFileKind::Archive);
1697a4013f0SLang Hames     break;
1707a4013f0SLang Hames   case file_magic::macho_object: {
1717a4013f0SLang Hames     if (LA != LoadArchives::Required) {
1727a4013f0SLang Hames       auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, true);
1737a4013f0SLang Hames       if (!CheckedObj)
1747a4013f0SLang Hames         return CheckedObj.takeError();
1757a4013f0SLang Hames       return std::make_pair(std::move(*CheckedObj),
1767a4013f0SLang Hames                             LinkableFileKind::RelocatableObject);
1777a4013f0SLang Hames     }
1787a4013f0SLang Hames     break;
1797a4013f0SLang Hames   }
1807a4013f0SLang Hames   default:
1817a4013f0SLang Hames     break;
1827a4013f0SLang Hames   }
1833e1d4ec6SLang Hames 
1847a4013f0SLang Hames   auto FT = [&] {
1857a4013f0SLang Hames     switch (LA) {
1867a4013f0SLang Hames     case LoadArchives::Never:
1877a4013f0SLang Hames       return "a mach-o relocatable object file";
1887a4013f0SLang Hames     case LoadArchives::Allowed:
1897a4013f0SLang Hames       return "a mach-o relocatable object file or archive";
1907a4013f0SLang Hames     case LoadArchives::Required:
1917a4013f0SLang Hames       return "an archive";
1927a4013f0SLang Hames     }
1938a50e35aSSimon Pilgrim     llvm_unreachable("Unknown LoadArchives enum");
1947a4013f0SLang Hames   };
1957a4013f0SLang Hames 
1967a4013f0SLang Hames   return make_error<StringError>(TT.getArchName() + " slice of " + UBPath +
1977a4013f0SLang Hames                                      " does not contain " + FT(),
1987a4013f0SLang Hames                                  inconvertibleErrorCode());
1993e1d4ec6SLang Hames }
2003e1d4ec6SLang Hames 
2013e1d4ec6SLang Hames Expected<std::pair<size_t, size_t>>
2023e1d4ec6SLang Hames getMachOSliceRangeForTriple(object::MachOUniversalBinary &UB,
2033e1d4ec6SLang Hames                             const Triple &TT) {
2043e1d4ec6SLang Hames 
2053e1d4ec6SLang Hames   for (const auto &Obj : UB.objects()) {
2063e1d4ec6SLang Hames     auto ObjTT = Obj.getTriple();
2073e1d4ec6SLang Hames     if (ObjTT.getArch() == TT.getArch() &&
2083e1d4ec6SLang Hames         ObjTT.getSubArch() == TT.getSubArch() &&
2093e1d4ec6SLang Hames         (TT.getVendor() == Triple::UnknownVendor ||
2103e1d4ec6SLang Hames          ObjTT.getVendor() == TT.getVendor())) {
2113e1d4ec6SLang Hames       // We found a match. Return the range for the slice.
2123e1d4ec6SLang Hames       return std::make_pair(Obj.getOffset(), Obj.getSize());
2133e1d4ec6SLang Hames     }
2143e1d4ec6SLang Hames   }
2153e1d4ec6SLang Hames 
2163e1d4ec6SLang Hames   return make_error<StringError>(Twine("Universal binary ") + UB.getFileName() +
2173e1d4ec6SLang Hames                                      " does not contain a slice for " +
2183e1d4ec6SLang Hames                                      TT.str(),
2193e1d4ec6SLang Hames                                  inconvertibleErrorCode());
2203e1d4ec6SLang Hames }
2213e1d4ec6SLang Hames 
2223e1d4ec6SLang Hames Expected<std::pair<size_t, size_t>>
2233e1d4ec6SLang Hames getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT) {
2243e1d4ec6SLang Hames 
2253e1d4ec6SLang Hames   auto UB = object::MachOUniversalBinary::create(UBBuf);
2263e1d4ec6SLang Hames   if (!UB)
2273e1d4ec6SLang Hames     return UB.takeError();
2283e1d4ec6SLang Hames 
2293e1d4ec6SLang Hames   return getMachOSliceRangeForTriple(**UB, TT);
2303e1d4ec6SLang Hames }
2313e1d4ec6SLang Hames 
232*7937fe1aSLang Hames Error ForceLoadMachOArchiveMembers::operator()(MemoryBufferRef MemberBuf) {
233*7937fe1aSLang Hames   if (!ObjCOnly)
234*7937fe1aSLang Hames     return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf));
235*7937fe1aSLang Hames 
236*7937fe1aSLang Hames   // We need to check whether this archive member contains any Objective-C
237*7937fe1aSLang Hames   // or Swift metadata.
238*7937fe1aSLang Hames 
239*7937fe1aSLang Hames   auto Obj = object::ObjectFile::createObjectFile(MemberBuf);
240*7937fe1aSLang Hames   if (!Obj) {
241*7937fe1aSLang Hames     // We silently ignore invalid files.
242*7937fe1aSLang Hames     consumeError(Obj.takeError());
243*7937fe1aSLang Hames     return Error::success();
244*7937fe1aSLang Hames   }
245*7937fe1aSLang Hames 
246*7937fe1aSLang Hames   if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&**Obj)) {
247*7937fe1aSLang Hames     // Load the object if any recognized special section is present.
248*7937fe1aSLang Hames     for (auto Sec : MachOObj->sections()) {
249*7937fe1aSLang Hames       auto SegName =
250*7937fe1aSLang Hames           MachOObj->getSectionFinalSegmentName(Sec.getRawDataRefImpl());
251*7937fe1aSLang Hames       if (auto SecName = Sec.getName()) {
252*7937fe1aSLang Hames         if (*SecName == "__objc_classlist" || *SecName == "__objc_protolist" ||
253*7937fe1aSLang Hames             *SecName == "__objc_clsrolist" || *SecName == "__objc_catlist" ||
254*7937fe1aSLang Hames             *SecName == "__objc_catlist2" || *SecName == "__objc_nlcatlist" ||
255*7937fe1aSLang Hames             (SegName == "__TEXT" && (*SecName).starts_with("__swift") &&
256*7937fe1aSLang Hames              *SecName != "__swift_modhash"))
257*7937fe1aSLang Hames           return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf));
258*7937fe1aSLang Hames       } else
259*7937fe1aSLang Hames         return SecName.takeError();
260*7937fe1aSLang Hames     }
261*7937fe1aSLang Hames   }
262*7937fe1aSLang Hames 
263*7937fe1aSLang Hames   return Error::success();
264*7937fe1aSLang Hames }
265*7937fe1aSLang Hames 
2663e1d4ec6SLang Hames } // End namespace orc.
2673e1d4ec6SLang Hames } // End namespace llvm.
268