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