1 //===----------------- MachO.cpp - MachO format utilities -----------------===// 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 #include "llvm/ExecutionEngine/Orc/MachO.h" 10 11 #include "llvm/ADT/ScopeExit.h" 12 #include "llvm/BinaryFormat/MachO.h" 13 #include "llvm/ExecutionEngine/Orc/Layer.h" 14 #include "llvm/Object/MachOUniversal.h" 15 #include "llvm/Support/FileSystem.h" 16 17 #define DEBUG_TYPE "orc" 18 19 namespace llvm { 20 namespace orc { 21 22 static std::string objDesc(const MemoryBufferRef &Obj, const Triple &TT, 23 bool ObjIsSlice) { 24 std::string Desc; 25 if (ObjIsSlice) 26 Desc += (TT.getArchName() + " slice of universal binary").str(); 27 Desc += Obj.getBufferIdentifier(); 28 return Desc; 29 } 30 31 template <typename HeaderType> 32 static Error checkMachORelocatableObject(MemoryBufferRef Obj, 33 bool SwapEndianness, const Triple &TT, 34 bool ObjIsSlice) { 35 StringRef Data = Obj.getBuffer(); 36 37 HeaderType Hdr; 38 memcpy(&Hdr, Data.data(), sizeof(HeaderType)); 39 40 if (SwapEndianness) 41 swapStruct(Hdr); 42 43 if (Hdr.filetype != MachO::MH_OBJECT) 44 return make_error<StringError>(objDesc(Obj, TT, ObjIsSlice) + 45 " is not a MachO relocatable object", 46 inconvertibleErrorCode()); 47 48 auto ObjArch = object::MachOObjectFile::getArch(Hdr.cputype, Hdr.cpusubtype); 49 if (ObjArch != TT.getArch()) 50 return make_error<StringError>( 51 objDesc(Obj, TT, ObjIsSlice) + Triple::getArchTypeName(ObjArch) + 52 ", cannot be loaded into " + TT.str() + " process", 53 inconvertibleErrorCode()); 54 55 return Error::success(); 56 } 57 58 Error checkMachORelocatableObject(MemoryBufferRef Obj, const Triple &TT, 59 bool ObjIsSlice) { 60 StringRef Data = Obj.getBuffer(); 61 62 if (Data.size() < 4) 63 return make_error<StringError>( 64 objDesc(Obj, TT, ObjIsSlice) + 65 " is not a valid MachO relocatable object file (truncated header)", 66 inconvertibleErrorCode()); 67 68 uint32_t Magic; 69 memcpy(&Magic, Data.data(), sizeof(uint32_t)); 70 71 switch (Magic) { 72 case MachO::MH_MAGIC: 73 case MachO::MH_CIGAM: 74 return checkMachORelocatableObject<MachO::mach_header>( 75 std::move(Obj), Magic == MachO::MH_CIGAM, TT, ObjIsSlice); 76 case MachO::MH_MAGIC_64: 77 case MachO::MH_CIGAM_64: 78 return checkMachORelocatableObject<MachO::mach_header_64>( 79 std::move(Obj), Magic == MachO::MH_CIGAM_64, TT, ObjIsSlice); 80 default: 81 return make_error<StringError>( 82 objDesc(Obj, TT, ObjIsSlice) + 83 " is not a valid MachO relocatable object (bad magic value)", 84 inconvertibleErrorCode()); 85 } 86 } 87 88 Expected<std::unique_ptr<MemoryBuffer>> 89 checkMachORelocatableObject(std::unique_ptr<MemoryBuffer> Obj, const Triple &TT, 90 bool ObjIsSlice) { 91 if (auto Err = 92 checkMachORelocatableObject(Obj->getMemBufferRef(), TT, ObjIsSlice)) 93 return std::move(Err); 94 return std::move(Obj); 95 } 96 97 Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>> 98 loadMachORelocatableObject(StringRef Path, const Triple &TT, LoadArchives LA, 99 std::optional<StringRef> IdentifierOverride) { 100 assert((TT.getObjectFormat() == Triple::UnknownObjectFormat || 101 TT.getObjectFormat() == Triple::MachO) && 102 "TT must specify MachO or Unknown object format"); 103 104 if (!IdentifierOverride) 105 IdentifierOverride = Path; 106 107 Expected<sys::fs::file_t> FDOrErr = 108 sys::fs::openNativeFileForRead(Path, sys::fs::OF_None); 109 if (!FDOrErr) 110 return createFileError(Path, FDOrErr.takeError()); 111 sys::fs::file_t FD = *FDOrErr; 112 auto CloseFile = make_scope_exit([&]() { sys::fs::closeFile(FD); }); 113 114 auto Buf = 115 MemoryBuffer::getOpenFile(FD, *IdentifierOverride, /*FileSize=*/-1); 116 if (!Buf) 117 return make_error<StringError>( 118 StringRef("Could not load MachO object at path ") + Path, 119 Buf.getError()); 120 121 switch (identify_magic((*Buf)->getBuffer())) { 122 case file_magic::macho_object: { 123 auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, false); 124 if (!CheckedObj) 125 return CheckedObj.takeError(); 126 return std::make_pair(std::move(*CheckedObj), 127 LinkableFileKind::RelocatableObject); 128 } 129 case file_magic::macho_universal_binary: 130 return loadLinkableSliceFromMachOUniversalBinary(FD, std::move(*Buf), TT, 131 LoadArchives::Never, Path, 132 *IdentifierOverride); 133 default: 134 return make_error<StringError>( 135 Path + " does not contain a relocatable object file compatible with " + 136 TT.str(), 137 inconvertibleErrorCode()); 138 } 139 } 140 141 Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>> 142 loadLinkableSliceFromMachOUniversalBinary(sys::fs::file_t FD, 143 std::unique_ptr<MemoryBuffer> UBBuf, 144 const Triple &TT, LoadArchives LA, 145 StringRef UBPath, 146 StringRef Identifier) { 147 148 auto UniversalBin = 149 object::MachOUniversalBinary::create(UBBuf->getMemBufferRef()); 150 if (!UniversalBin) 151 return UniversalBin.takeError(); 152 153 auto SliceRange = getMachOSliceRangeForTriple(**UniversalBin, TT); 154 if (!SliceRange) 155 return SliceRange.takeError(); 156 157 auto Buf = MemoryBuffer::getOpenFileSlice(FD, Identifier, SliceRange->second, 158 SliceRange->first); 159 if (!Buf) 160 return make_error<StringError>( 161 "Could not load " + TT.getArchName() + 162 " slice of MachO universal binary at path " + UBPath, 163 Buf.getError()); 164 165 switch (identify_magic((*Buf)->getBuffer())) { 166 case file_magic::archive: 167 if (LA != LoadArchives::Never) 168 return std::make_pair(std::move(*Buf), LinkableFileKind::Archive); 169 break; 170 case file_magic::macho_object: { 171 if (LA != LoadArchives::Required) { 172 auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, true); 173 if (!CheckedObj) 174 return CheckedObj.takeError(); 175 return std::make_pair(std::move(*CheckedObj), 176 LinkableFileKind::RelocatableObject); 177 } 178 break; 179 } 180 default: 181 break; 182 } 183 184 auto FT = [&] { 185 switch (LA) { 186 case LoadArchives::Never: 187 return "a mach-o relocatable object file"; 188 case LoadArchives::Allowed: 189 return "a mach-o relocatable object file or archive"; 190 case LoadArchives::Required: 191 return "an archive"; 192 } 193 llvm_unreachable("Unknown LoadArchives enum"); 194 }; 195 196 return make_error<StringError>(TT.getArchName() + " slice of " + UBPath + 197 " does not contain " + FT(), 198 inconvertibleErrorCode()); 199 } 200 201 Expected<std::pair<size_t, size_t>> 202 getMachOSliceRangeForTriple(object::MachOUniversalBinary &UB, 203 const Triple &TT) { 204 205 for (const auto &Obj : UB.objects()) { 206 auto ObjTT = Obj.getTriple(); 207 if (ObjTT.getArch() == TT.getArch() && 208 ObjTT.getSubArch() == TT.getSubArch() && 209 (TT.getVendor() == Triple::UnknownVendor || 210 ObjTT.getVendor() == TT.getVendor())) { 211 // We found a match. Return the range for the slice. 212 return std::make_pair(Obj.getOffset(), Obj.getSize()); 213 } 214 } 215 216 return make_error<StringError>(Twine("Universal binary ") + UB.getFileName() + 217 " does not contain a slice for " + 218 TT.str(), 219 inconvertibleErrorCode()); 220 } 221 222 Expected<std::pair<size_t, size_t>> 223 getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT) { 224 225 auto UB = object::MachOUniversalBinary::create(UBBuf); 226 if (!UB) 227 return UB.takeError(); 228 229 return getMachOSliceRangeForTriple(**UB, TT); 230 } 231 232 Error ForceLoadMachOArchiveMembers::operator()(MemoryBufferRef MemberBuf) { 233 if (!ObjCOnly) 234 return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf)); 235 236 // We need to check whether this archive member contains any Objective-C 237 // or Swift metadata. 238 239 auto Obj = object::ObjectFile::createObjectFile(MemberBuf); 240 if (!Obj) { 241 // We silently ignore invalid files. 242 consumeError(Obj.takeError()); 243 return Error::success(); 244 } 245 246 if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&**Obj)) { 247 // Load the object if any recognized special section is present. 248 for (auto Sec : MachOObj->sections()) { 249 auto SegName = 250 MachOObj->getSectionFinalSegmentName(Sec.getRawDataRefImpl()); 251 if (auto SecName = Sec.getName()) { 252 if (*SecName == "__objc_classlist" || *SecName == "__objc_protolist" || 253 *SecName == "__objc_clsrolist" || *SecName == "__objc_catlist" || 254 *SecName == "__objc_catlist2" || *SecName == "__objc_nlcatlist" || 255 (SegName == "__TEXT" && (*SecName).starts_with("__swift") && 256 *SecName != "__swift_modhash")) 257 return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf)); 258 } else 259 return SecName.takeError(); 260 } 261 } 262 263 return Error::success(); 264 } 265 266 } // End namespace orc. 267 } // End namespace llvm. 268