459 lines · cpp
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-exception6//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/BinaryFormat/Magic.h"14#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"15#include "llvm/ExecutionEngine/Orc/Layer.h"16#include "llvm/Object/MachOUniversal.h"17#include "llvm/Object/TapiUniversal.h"18#include "llvm/Support/FileSystem.h"19 20#define DEBUG_TYPE "orc"21 22namespace llvm {23namespace orc {24 25static std::string objDesc(const MemoryBufferRef &Obj, const Triple &TT,26 bool ObjIsSlice) {27 std::string Desc;28 if (ObjIsSlice)29 Desc += (TT.getArchName() + " slice of universal binary").str();30 Desc += Obj.getBufferIdentifier();31 return Desc;32}33 34template <typename HeaderType>35static Error checkMachORelocatableObject(MemoryBufferRef Obj,36 bool SwapEndianness, const Triple &TT,37 bool ObjIsSlice) {38 StringRef Data = Obj.getBuffer();39 40 HeaderType Hdr;41 memcpy(&Hdr, Data.data(), sizeof(HeaderType));42 43 if (SwapEndianness)44 swapStruct(Hdr);45 46 if (Hdr.filetype != MachO::MH_OBJECT)47 return make_error<StringError>(objDesc(Obj, TT, ObjIsSlice) +48 " is not a MachO relocatable object",49 inconvertibleErrorCode());50 51 auto ObjArch = object::MachOObjectFile::getArch(Hdr.cputype, Hdr.cpusubtype);52 if (ObjArch != TT.getArch())53 return make_error<StringError>(54 objDesc(Obj, TT, ObjIsSlice) + Triple::getArchTypeName(ObjArch) +55 ", cannot be loaded into " + TT.str() + " process",56 inconvertibleErrorCode());57 58 return Error::success();59}60 61Error checkMachORelocatableObject(MemoryBufferRef Obj, const Triple &TT,62 bool ObjIsSlice) {63 StringRef Data = Obj.getBuffer();64 65 if (Data.size() < 4)66 return make_error<StringError>(67 objDesc(Obj, TT, ObjIsSlice) +68 " is not a valid MachO relocatable object file (truncated header)",69 inconvertibleErrorCode());70 71 uint32_t Magic;72 memcpy(&Magic, Data.data(), sizeof(uint32_t));73 74 switch (Magic) {75 case MachO::MH_MAGIC:76 case MachO::MH_CIGAM:77 return checkMachORelocatableObject<MachO::mach_header>(78 std::move(Obj), Magic == MachO::MH_CIGAM, TT, ObjIsSlice);79 case MachO::MH_MAGIC_64:80 case MachO::MH_CIGAM_64:81 return checkMachORelocatableObject<MachO::mach_header_64>(82 std::move(Obj), Magic == MachO::MH_CIGAM_64, TT, ObjIsSlice);83 default:84 return make_error<StringError>(85 objDesc(Obj, TT, ObjIsSlice) +86 " is not a valid MachO relocatable object (bad magic value)",87 inconvertibleErrorCode());88 }89}90 91Expected<std::unique_ptr<MemoryBuffer>>92checkMachORelocatableObject(std::unique_ptr<MemoryBuffer> Obj, const Triple &TT,93 bool ObjIsSlice) {94 if (auto Err =95 checkMachORelocatableObject(Obj->getMemBufferRef(), TT, ObjIsSlice))96 return std::move(Err);97 return std::move(Obj);98}99 100Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>101loadMachORelocatableObject(StringRef Path, const Triple &TT, LoadArchives LA,102 std::optional<StringRef> IdentifierOverride) {103 assert((TT.getObjectFormat() == Triple::UnknownObjectFormat ||104 TT.getObjectFormat() == Triple::MachO) &&105 "TT must specify MachO or Unknown object format");106 107 if (!IdentifierOverride)108 IdentifierOverride = Path;109 110 Expected<sys::fs::file_t> FDOrErr =111 sys::fs::openNativeFileForRead(Path, sys::fs::OF_None);112 if (!FDOrErr)113 return createFileError(Path, FDOrErr.takeError());114 sys::fs::file_t FD = *FDOrErr;115 auto CloseFile = make_scope_exit([&]() { sys::fs::closeFile(FD); });116 117 auto Buf =118 MemoryBuffer::getOpenFile(FD, *IdentifierOverride, /*FileSize=*/-1);119 if (!Buf)120 return make_error<StringError>(121 StringRef("Could not load MachO object at path ") + Path,122 Buf.getError());123 124 switch (identify_magic((*Buf)->getBuffer())) {125 case file_magic::macho_object: {126 auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, false);127 if (!CheckedObj)128 return CheckedObj.takeError();129 return std::make_pair(std::move(*CheckedObj),130 LinkableFileKind::RelocatableObject);131 }132 case file_magic::macho_universal_binary:133 return loadLinkableSliceFromMachOUniversalBinary(FD, std::move(*Buf), TT,134 LoadArchives::Never, Path,135 *IdentifierOverride);136 default:137 return make_error<StringError>(138 Path + " does not contain a relocatable object file compatible with " +139 TT.str(),140 inconvertibleErrorCode());141 }142}143 144Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>145loadLinkableSliceFromMachOUniversalBinary(sys::fs::file_t FD,146 std::unique_ptr<MemoryBuffer> UBBuf,147 const Triple &TT, LoadArchives LA,148 StringRef UBPath,149 StringRef Identifier) {150 151 auto UniversalBin =152 object::MachOUniversalBinary::create(UBBuf->getMemBufferRef());153 if (!UniversalBin)154 return UniversalBin.takeError();155 156 auto SliceRange = getMachOSliceRangeForTriple(**UniversalBin, TT);157 if (!SliceRange)158 return SliceRange.takeError();159 160 auto Buf = MemoryBuffer::getOpenFileSlice(FD, Identifier, SliceRange->second,161 SliceRange->first);162 if (!Buf)163 return make_error<StringError>(164 "Could not load " + TT.getArchName() +165 " slice of MachO universal binary at path " + UBPath,166 Buf.getError());167 168 switch (identify_magic((*Buf)->getBuffer())) {169 case file_magic::archive:170 if (LA != LoadArchives::Never)171 return std::make_pair(std::move(*Buf), LinkableFileKind::Archive);172 break;173 case file_magic::macho_object: {174 if (LA != LoadArchives::Required) {175 auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, true);176 if (!CheckedObj)177 return CheckedObj.takeError();178 return std::make_pair(std::move(*CheckedObj),179 LinkableFileKind::RelocatableObject);180 }181 break;182 }183 default:184 break;185 }186 187 auto FT = [&] {188 switch (LA) {189 case LoadArchives::Never:190 return "a mach-o relocatable object file";191 case LoadArchives::Allowed:192 return "a mach-o relocatable object file or archive";193 case LoadArchives::Required:194 return "an archive";195 }196 llvm_unreachable("Unknown LoadArchives enum");197 };198 199 return make_error<StringError>(TT.getArchName() + " slice of " + UBPath +200 " does not contain " + FT(),201 inconvertibleErrorCode());202}203 204Expected<std::pair<size_t, size_t>>205getMachOSliceRangeForTriple(object::MachOUniversalBinary &UB,206 const Triple &TT) {207 208 for (const auto &Obj : UB.objects()) {209 auto ObjTT = Obj.getTriple();210 if (ObjTT.getArch() == TT.getArch() &&211 ObjTT.getSubArch() == TT.getSubArch() &&212 (TT.getVendor() == Triple::UnknownVendor ||213 ObjTT.getVendor() == TT.getVendor())) {214 // We found a match. Return the range for the slice.215 return std::make_pair(Obj.getOffset(), Obj.getSize());216 }217 }218 219 return make_error<StringError>(Twine("Universal binary ") + UB.getFileName() +220 " does not contain a slice for " +221 TT.str(),222 inconvertibleErrorCode());223}224 225Expected<std::pair<size_t, size_t>>226getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT) {227 228 auto UB = object::MachOUniversalBinary::create(UBBuf);229 if (!UB)230 return UB.takeError();231 232 return getMachOSliceRangeForTriple(**UB, TT);233}234 235Expected<bool> ForceLoadMachOArchiveMembers::operator()(236 object::Archive &A, MemoryBufferRef MemberBuf, size_t Index) {237 238 auto LoadMember = [&]() {239 return StaticLibraryDefinitionGenerator::createMemberBuffer(A, MemberBuf,240 Index);241 };242 243 if (!ObjCOnly) {244 // If we're loading all files then just load the buffer immediately. Return245 // false to indicate that there's no further loading to do here.246 if (auto Err = L.add(JD, LoadMember()))247 return Err;248 return false;249 }250 251 // We need to check whether this archive member contains any Objective-C252 // or Swift metadata.253 auto Obj = object::ObjectFile::createObjectFile(MemberBuf);254 if (!Obj) {255 // Invalid files are not loadable, but don't invalidate the archive.256 consumeError(Obj.takeError());257 return false;258 }259 260 if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&**Obj)) {261 // Load the object if any recognized special section is present.262 for (auto Sec : MachOObj->sections()) {263 auto SegName =264 MachOObj->getSectionFinalSegmentName(Sec.getRawDataRefImpl());265 if (auto SecName = Sec.getName()) {266 if (*SecName == "__objc_classlist" || *SecName == "__objc_protolist" ||267 *SecName == "__objc_clsrolist" || *SecName == "__objc_catlist" ||268 *SecName == "__objc_catlist2" || *SecName == "__objc_nlcatlist" ||269 (SegName == "__TEXT" && (*SecName).starts_with("__swift") &&270 *SecName != "__swift_modhash")) {271 if (auto Err = L.add(JD, LoadMember()))272 return Err;273 return false;274 }275 } else276 return SecName.takeError();277 }278 }279 280 // This is an object file but we didn't load it, so return true to indicate281 // that it's still loadable.282 return true;283}284 285SmallVector<std::pair<uint32_t, uint32_t>>286noFallbackArchs(uint32_t CPUType, uint32_t CPUSubType) {287 SmallVector<std::pair<uint32_t, uint32_t>> Result;288 Result.push_back({CPUType, CPUSubType});289 return Result;290}291 292SmallVector<std::pair<uint32_t, uint32_t>>293standardMachOFallbackArchs(uint32_t CPUType, uint32_t CPUSubType) {294 SmallVector<std::pair<uint32_t, uint32_t>> Archs;295 296 // Match given CPU type/subtype first.297 Archs.push_back({CPUType, CPUSubType});298 299 switch (CPUType) {300 case MachO::CPU_TYPE_ARM64:301 // Handle arm64 variants.302 switch (CPUSubType) {303 case MachO::CPU_SUBTYPE_ARM64_ALL:304 Archs.push_back({CPUType, MachO::CPU_SUBTYPE_ARM64E});305 break;306 default:307 break;308 }309 break;310 default:311 break;312 }313 314 return Archs;315}316 317Expected<SymbolNameSet>318getDylibInterfaceFromDylib(ExecutionSession &ES, Twine Path,319 GetFallbackArchsFn GetFallbackArchs) {320 auto InitCPUType = MachO::getCPUType(ES.getTargetTriple());321 if (!InitCPUType)322 return InitCPUType.takeError();323 324 auto InitCPUSubType = MachO::getCPUSubType(ES.getTargetTriple());325 if (!InitCPUSubType)326 return InitCPUSubType.takeError();327 328 auto Buf = MemoryBuffer::getFile(Path);329 if (!Buf)330 return createFileError(Path, Buf.getError());331 332 auto BinFile = object::createBinary((*Buf)->getMemBufferRef());333 if (!BinFile)334 return BinFile.takeError();335 336 std::unique_ptr<object::MachOObjectFile> MachOFile;337 if (isa<object::MachOObjectFile>(**BinFile)) {338 MachOFile.reset(dyn_cast<object::MachOObjectFile>(BinFile->release()));339 340 // TODO: Check that dylib arch is compatible.341 } else if (auto *MachOUni =342 dyn_cast<object::MachOUniversalBinary>(BinFile->get())) {343 SmallVector<std::pair<uint32_t, uint32_t>> ArchsToTry;344 if (GetFallbackArchs)345 ArchsToTry = GetFallbackArchs(*InitCPUType, *InitCPUSubType);346 else347 ArchsToTry.push_back({*InitCPUType, *InitCPUSubType});348 349 for (auto &[CPUType, CPUSubType] : ArchsToTry) {350 for (auto &O : MachOUni->objects()) {351 if (O.getCPUType() == CPUType &&352 (O.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == CPUSubType) {353 if (auto Obj = O.getAsObjectFile())354 MachOFile = std::move(*Obj);355 else356 return Obj.takeError();357 break;358 }359 }360 if (MachOFile) // If found, break out.361 break;362 }363 if (!MachOFile)364 return make_error<StringError>(365 "MachO universal binary at " + Path +366 " does not contain a compatible slice for " +367 ES.getTargetTriple().str(),368 inconvertibleErrorCode());369 } else370 return make_error<StringError>("File at " + Path + " is not a MachO",371 inconvertibleErrorCode());372 373 if (MachOFile->getHeader().filetype != MachO::MH_DYLIB)374 return make_error<StringError>("MachO at " + Path + " is not a dylib",375 inconvertibleErrorCode());376 377 SymbolNameSet Symbols;378 for (auto &Sym : MachOFile->symbols()) {379 if (auto Name = Sym.getName())380 Symbols.insert(ES.intern(*Name));381 else382 return Name.takeError();383 }384 385 return std::move(Symbols);386}387 388Expected<SymbolNameSet>389getDylibInterfaceFromTapiFile(ExecutionSession &ES, Twine Path,390 GetFallbackArchsFn GetFallbackArchs) {391 SymbolNameSet Symbols;392 393 auto TapiFileBuffer = MemoryBuffer::getFile(Path);394 if (!TapiFileBuffer)395 return createFileError(Path, TapiFileBuffer.getError());396 397 auto Tapi =398 object::TapiUniversal::create((*TapiFileBuffer)->getMemBufferRef());399 if (!Tapi)400 return Tapi.takeError();401 402 auto InitCPUType = MachO::getCPUType(ES.getTargetTriple());403 if (!InitCPUType)404 return InitCPUType.takeError();405 406 auto InitCPUSubType = MachO::getCPUSubType(ES.getTargetTriple());407 if (!InitCPUSubType)408 return InitCPUSubType.takeError();409 410 SmallVector<std::pair<uint32_t, uint32_t>> ArchsToTry;411 if (GetFallbackArchs)412 ArchsToTry = GetFallbackArchs(*InitCPUType, *InitCPUSubType);413 else414 ArchsToTry.push_back({*InitCPUType, *InitCPUSubType});415 416 auto &IF = (*Tapi)->getInterfaceFile();417 418 auto ArchSet = IF.getArchitectures();419 for (auto [CPUType, CPUSubType] : ArchsToTry) {420 auto A = MachO::getArchitectureFromCpuType(CPUType, CPUSubType);421 if (ArchSet.has(A)) {422 if (auto Interface = IF.extract(A)) {423 for (auto *Sym : (*Interface)->exports())424 Symbols.insert(ES.intern(Sym->getName()));425 return Symbols;426 } else427 return Interface.takeError();428 }429 }430 431 return make_error<StringError>(432 "MachO interface file at " + Path +433 " does not contain a compatible slice for " +434 ES.getTargetTriple().str(),435 inconvertibleErrorCode());436}437 438Expected<SymbolNameSet> getDylibInterface(ExecutionSession &ES, Twine Path,439 GetFallbackArchsFn GetFallbackArchs) {440 file_magic Magic;441 if (auto EC = identify_magic(Path, Magic))442 return createFileError(Path, EC);443 444 switch (Magic) {445 case file_magic::macho_universal_binary:446 case file_magic::macho_dynamically_linked_shared_lib:447 return getDylibInterfaceFromDylib(ES, Path, std::move(GetFallbackArchs));448 case file_magic::tapi_file:449 return getDylibInterfaceFromTapiFile(ES, Path, std::move(GetFallbackArchs));450 default:451 return make_error<StringError>("Cannot get interface for " + Path +452 " unrecognized file type",453 inconvertibleErrorCode());454 }455}456 457} // End namespace orc.458} // End namespace llvm.459