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