1 //===- DylibReader.cpp -------------- TAPI MachO Dylib Reader --*- C++ -*-===// 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 /// Implements the TAPI Reader for Mach-O dynamic libraries. 10 /// 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/TextAPI/DylibReader.h" 14 #include "llvm/ADT/STLExtras.h" 15 #include "llvm/ADT/StringMap.h" 16 #include "llvm/Object/Binary.h" 17 #include "llvm/Object/MachOUniversal.h" 18 #include "llvm/Support/Endian.h" 19 #include "llvm/TargetParser/Triple.h" 20 #include "llvm/TextAPI/InterfaceFile.h" 21 #include "llvm/TextAPI/RecordsSlice.h" 22 #include "llvm/TextAPI/TextAPIError.h" 23 #include <iomanip> 24 #include <set> 25 #include <sstream> 26 #include <string> 27 #include <tuple> 28 29 using namespace llvm; 30 using namespace llvm::object; 31 using namespace llvm::MachO; 32 using namespace llvm::MachO::DylibReader; 33 34 using TripleVec = std::vector<Triple>; 35 static typename TripleVec::iterator emplace(TripleVec &Container, Triple &&T) { 36 auto I = partition_point(Container, [=](const Triple &CT) { 37 return std::forward_as_tuple(CT.getArch(), CT.getOS(), 38 CT.getEnvironment()) < 39 std::forward_as_tuple(T.getArch(), T.getOS(), T.getEnvironment()); 40 }); 41 42 if (I != Container.end() && *I == T) 43 return I; 44 return Container.emplace(I, T); 45 } 46 47 static TripleVec constructTriples(MachOObjectFile *Obj, 48 const Architecture ArchT) { 49 auto getOSVersionStr = [](uint32_t V) { 50 PackedVersion OSVersion(V); 51 std::string Vers; 52 raw_string_ostream VStream(Vers); 53 VStream << OSVersion; 54 return VStream.str(); 55 }; 56 auto getOSVersion = [&](const MachOObjectFile::LoadCommandInfo &cmd) { 57 auto Vers = Obj->getVersionMinLoadCommand(cmd); 58 return getOSVersionStr(Vers.version); 59 }; 60 61 TripleVec Triples; 62 bool IsIntel = ArchitectureSet(ArchT).hasX86(); 63 auto Arch = getArchitectureName(ArchT); 64 65 for (const auto &cmd : Obj->load_commands()) { 66 std::string OSVersion; 67 switch (cmd.C.cmd) { 68 case MachO::LC_VERSION_MIN_MACOSX: 69 OSVersion = getOSVersion(cmd); 70 emplace(Triples, {Arch, "apple", "macos" + OSVersion}); 71 break; 72 case MachO::LC_VERSION_MIN_IPHONEOS: 73 OSVersion = getOSVersion(cmd); 74 if (IsIntel) 75 emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"}); 76 else 77 emplace(Triples, {Arch, "apple", "ios" + OSVersion}); 78 break; 79 case MachO::LC_VERSION_MIN_TVOS: 80 OSVersion = getOSVersion(cmd); 81 if (IsIntel) 82 emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"}); 83 else 84 emplace(Triples, {Arch, "apple", "tvos" + OSVersion}); 85 break; 86 case MachO::LC_VERSION_MIN_WATCHOS: 87 OSVersion = getOSVersion(cmd); 88 if (IsIntel) 89 emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"}); 90 else 91 emplace(Triples, {Arch, "apple", "watchos" + OSVersion}); 92 break; 93 case MachO::LC_BUILD_VERSION: { 94 OSVersion = getOSVersionStr(Obj->getBuildVersionLoadCommand(cmd).minos); 95 switch (Obj->getBuildVersionLoadCommand(cmd).platform) { 96 case MachO::PLATFORM_MACOS: 97 emplace(Triples, {Arch, "apple", "macos" + OSVersion}); 98 break; 99 case MachO::PLATFORM_IOS: 100 emplace(Triples, {Arch, "apple", "ios" + OSVersion}); 101 break; 102 case MachO::PLATFORM_TVOS: 103 emplace(Triples, {Arch, "apple", "tvos" + OSVersion}); 104 break; 105 case MachO::PLATFORM_WATCHOS: 106 emplace(Triples, {Arch, "apple", "watchos" + OSVersion}); 107 break; 108 case MachO::PLATFORM_BRIDGEOS: 109 emplace(Triples, {Arch, "apple", "bridgeos" + OSVersion}); 110 break; 111 case MachO::PLATFORM_MACCATALYST: 112 emplace(Triples, {Arch, "apple", "ios" + OSVersion, "macabi"}); 113 break; 114 case MachO::PLATFORM_IOSSIMULATOR: 115 emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"}); 116 break; 117 case MachO::PLATFORM_TVOSSIMULATOR: 118 emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"}); 119 break; 120 case MachO::PLATFORM_WATCHOSSIMULATOR: 121 emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"}); 122 break; 123 case MachO::PLATFORM_DRIVERKIT: 124 emplace(Triples, {Arch, "apple", "driverkit" + OSVersion}); 125 break; 126 default: 127 break; // Skip any others. 128 } 129 break; 130 } 131 default: 132 break; 133 } 134 } 135 136 // Record unknown platform for older binaries that don't enforce platform 137 // load commands. 138 if (Triples.empty()) 139 emplace(Triples, {Arch, "apple", "unknown"}); 140 141 return Triples; 142 } 143 144 static Error readMachOHeader(MachOObjectFile *Obj, RecordsSlice &Slice) { 145 auto H = Obj->getHeader(); 146 auto &BA = Slice.getBinaryAttrs(); 147 148 switch (H.filetype) { 149 default: 150 llvm_unreachable("unsupported binary type"); 151 case MachO::MH_DYLIB: 152 BA.File = FileType::MachO_DynamicLibrary; 153 break; 154 case MachO::MH_DYLIB_STUB: 155 BA.File = FileType::MachO_DynamicLibrary_Stub; 156 break; 157 case MachO::MH_BUNDLE: 158 BA.File = FileType::MachO_Bundle; 159 break; 160 } 161 162 if (H.flags & MachO::MH_TWOLEVEL) 163 BA.TwoLevelNamespace = true; 164 if (H.flags & MachO::MH_APP_EXTENSION_SAFE) 165 BA.AppExtensionSafe = true; 166 167 for (const auto &LCI : Obj->load_commands()) { 168 switch (LCI.C.cmd) { 169 case MachO::LC_ID_DYLIB: { 170 auto DLLC = Obj->getDylibIDLoadCommand(LCI); 171 BA.InstallName = Slice.copyString(LCI.Ptr + DLLC.dylib.name); 172 BA.CurrentVersion = DLLC.dylib.current_version; 173 BA.CompatVersion = DLLC.dylib.compatibility_version; 174 break; 175 } 176 case MachO::LC_REEXPORT_DYLIB: { 177 auto DLLC = Obj->getDylibIDLoadCommand(LCI); 178 BA.RexportedLibraries.emplace_back( 179 Slice.copyString(LCI.Ptr + DLLC.dylib.name)); 180 break; 181 } 182 case MachO::LC_SUB_FRAMEWORK: { 183 auto SFC = Obj->getSubFrameworkCommand(LCI); 184 BA.ParentUmbrella = Slice.copyString(LCI.Ptr + SFC.umbrella); 185 break; 186 } 187 case MachO::LC_SUB_CLIENT: { 188 auto SCLC = Obj->getSubClientCommand(LCI); 189 BA.AllowableClients.emplace_back(Slice.copyString(LCI.Ptr + SCLC.client)); 190 break; 191 } 192 case MachO::LC_UUID: { 193 auto UUIDLC = Obj->getUuidCommand(LCI); 194 std::stringstream Stream; 195 for (unsigned I = 0; I < 16; ++I) { 196 if (I == 4 || I == 6 || I == 8 || I == 10) 197 Stream << '-'; 198 Stream << std::setfill('0') << std::setw(2) << std::uppercase 199 << std::hex << static_cast<int>(UUIDLC.uuid[I]); 200 } 201 BA.UUID = Slice.copyString(Stream.str()); 202 break; 203 } 204 case MachO::LC_RPATH: { 205 auto RPLC = Obj->getRpathCommand(LCI); 206 BA.RPaths.emplace_back(Slice.copyString(LCI.Ptr + RPLC.path)); 207 break; 208 } 209 case MachO::LC_SEGMENT_SPLIT_INFO: { 210 auto SSILC = Obj->getLinkeditDataLoadCommand(LCI); 211 if (SSILC.datasize == 0) 212 BA.OSLibNotForSharedCache = true; 213 break; 214 } 215 default: 216 break; 217 } 218 } 219 220 for (auto &Sect : Obj->sections()) { 221 auto SectName = Sect.getName(); 222 if (!SectName) 223 return SectName.takeError(); 224 if (*SectName != "__objc_imageinfo" && *SectName != "__image_info") 225 continue; 226 227 auto Content = Sect.getContents(); 228 if (!Content) 229 return Content.takeError(); 230 231 if ((Content->size() >= 8) && (Content->front() == 0)) { 232 uint32_t Flags; 233 if (Obj->isLittleEndian()) { 234 auto *p = 235 reinterpret_cast<const support::ulittle32_t *>(Content->data() + 4); 236 Flags = *p; 237 } else { 238 auto *p = 239 reinterpret_cast<const support::ubig32_t *>(Content->data() + 4); 240 Flags = *p; 241 } 242 BA.SwiftABI = (Flags >> 8) & 0xFF; 243 } 244 } 245 return Error::success(); 246 } 247 248 static Error readSymbols(MachOObjectFile *Obj, RecordsSlice &Slice, 249 const ParseOption &Opt) { 250 251 auto parseExport = [](const auto ExportFlags, 252 auto Addr) -> std::tuple<SymbolFlags, RecordLinkage> { 253 SymbolFlags Flags = SymbolFlags::None; 254 switch (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) { 255 case MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR: 256 if (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) 257 Flags |= SymbolFlags::WeakDefined; 258 break; 259 case MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: 260 Flags |= SymbolFlags::ThreadLocalValue; 261 break; 262 } 263 264 RecordLinkage Linkage = (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) 265 ? RecordLinkage::Rexported 266 : RecordLinkage::Exported; 267 return {Flags, Linkage}; 268 }; 269 270 Error Err = Error::success(); 271 272 StringMap<std::pair<SymbolFlags, RecordLinkage>> Exports; 273 // Collect symbols from export trie first. Sometimes, there are more exports 274 // in the trie than in n-list due to stripping. This is common for swift 275 // mangled symbols. 276 for (auto &Sym : Obj->exports(Err)) { 277 auto [Flags, Linkage] = parseExport(Sym.flags(), Sym.address()); 278 Slice.addRecord(Sym.name(), Flags, GlobalRecord::Kind::Unknown, Linkage); 279 Exports[Sym.name()] = {Flags, Linkage}; 280 } 281 282 for (const auto &Sym : Obj->symbols()) { 283 auto FlagsOrErr = Sym.getFlags(); 284 if (!FlagsOrErr) 285 return FlagsOrErr.takeError(); 286 auto Flags = *FlagsOrErr; 287 288 auto NameOrErr = Sym.getName(); 289 if (!NameOrErr) 290 return NameOrErr.takeError(); 291 auto Name = *NameOrErr; 292 293 RecordLinkage Linkage = RecordLinkage::Unknown; 294 SymbolFlags RecordFlags = SymbolFlags::None; 295 296 if (Opt.Undefineds && (Flags & SymbolRef::SF_Undefined)) { 297 Linkage = RecordLinkage::Undefined; 298 if (Flags & SymbolRef::SF_Weak) 299 RecordFlags |= SymbolFlags::WeakReferenced; 300 } else if (Flags & SymbolRef::SF_Exported) { 301 auto Exp = Exports.find(Name); 302 // This should never be possible when binaries are produced with Apple 303 // linkers. However it is possible to craft dylibs where the export trie 304 // is either malformed or has conflicting symbols compared to n_list. 305 if (Exp != Exports.end()) 306 std::tie(RecordFlags, Linkage) = Exp->second; 307 else 308 Linkage = RecordLinkage::Exported; 309 } else if (Flags & SymbolRef::SF_Hidden) { 310 Linkage = RecordLinkage::Internal; 311 } else 312 continue; 313 314 auto TypeOrErr = Sym.getType(); 315 if (!TypeOrErr) 316 return TypeOrErr.takeError(); 317 auto Type = *TypeOrErr; 318 319 GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function) 320 ? GlobalRecord::Kind::Function 321 : GlobalRecord::Kind::Variable; 322 323 if (GV == GlobalRecord::Kind::Function) 324 RecordFlags |= SymbolFlags::Text; 325 else 326 RecordFlags |= SymbolFlags::Data; 327 328 Slice.addRecord(Name, RecordFlags, GV, Linkage); 329 } 330 return Err; 331 } 332 333 static Error load(MachOObjectFile *Obj, RecordsSlice &Slice, 334 const ParseOption &Opt, const Architecture Arch) { 335 if (Arch == AK_unknown) 336 return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget); 337 338 if (Opt.MachOHeader) 339 if (auto Err = readMachOHeader(Obj, Slice)) 340 return Err; 341 342 if (Opt.SymbolTable) 343 if (auto Err = readSymbols(Obj, Slice, Opt)) 344 return Err; 345 346 return Error::success(); 347 } 348 349 Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer, 350 const ParseOption &Opt) { 351 Records Results; 352 353 auto BinOrErr = createBinary(Buffer); 354 if (!BinOrErr) 355 return BinOrErr.takeError(); 356 357 Binary &Bin = *BinOrErr.get(); 358 if (auto *Obj = dyn_cast<MachOObjectFile>(&Bin)) { 359 const auto Arch = getArchitectureFromCpuType(Obj->getHeader().cputype, 360 Obj->getHeader().cpusubtype); 361 if (!Opt.Archs.has(Arch)) 362 return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture); 363 364 auto Triples = constructTriples(Obj, Arch); 365 for (const auto &T : Triples) { 366 if (mapToPlatformType(T) == PLATFORM_UNKNOWN) 367 return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget); 368 Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T}))); 369 if (auto Err = load(Obj, *Results.back(), Opt, Arch)) 370 return std::move(Err); 371 Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier(); 372 } 373 return Results; 374 } 375 376 // Only expect MachO universal binaries at this point. 377 assert(isa<MachOUniversalBinary>(&Bin) && 378 "Expected a MachO universal binary."); 379 auto *UB = cast<MachOUniversalBinary>(&Bin); 380 381 for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) { 382 // Skip architecture if not requested. 383 auto Arch = 384 getArchitectureFromCpuType(OI->getCPUType(), OI->getCPUSubType()); 385 if (!Opt.Archs.has(Arch)) 386 continue; 387 388 // Skip unknown architectures. 389 if (Arch == AK_unknown) 390 continue; 391 392 // This can fail if the object is an archive. 393 auto ObjOrErr = OI->getAsObjectFile(); 394 395 // Skip the archive and consume the error. 396 if (!ObjOrErr) { 397 consumeError(ObjOrErr.takeError()); 398 continue; 399 } 400 401 auto &Obj = *ObjOrErr.get(); 402 switch (Obj.getHeader().filetype) { 403 default: 404 break; 405 case MachO::MH_BUNDLE: 406 case MachO::MH_DYLIB: 407 case MachO::MH_DYLIB_STUB: 408 for (const auto &T : constructTriples(&Obj, Arch)) { 409 Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T}))); 410 if (auto Err = load(&Obj, *Results.back(), Opt, Arch)) 411 return std::move(Err); 412 Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier(); 413 } 414 break; 415 } 416 } 417 418 if (Results.empty()) 419 return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults); 420 return Results; 421 } 422 423 Expected<std::unique_ptr<InterfaceFile>> 424 DylibReader::get(MemoryBufferRef Buffer) { 425 ParseOption Options; 426 auto SlicesOrErr = readFile(Buffer, Options); 427 if (!SlicesOrErr) 428 return SlicesOrErr.takeError(); 429 430 return convertToInterfaceFile(*SlicesOrErr); 431 } 432