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