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