| 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/DebugInfo/DWARF/DWARFCompileUnit.h" |
| 16 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| 17 | #include "llvm/Object/Binary.h" |
| 18 | #include "llvm/Object/MachOUniversal.h" |
| 19 | #include "llvm/Support/Endian.h" |
| 20 | #include "llvm/TargetParser/Triple.h" |
| 21 | #include "llvm/TextAPI/InterfaceFile.h" |
| 22 | #include "llvm/TextAPI/RecordsSlice.h" |
| 23 | #include "llvm/TextAPI/TextAPIError.h" |
| 24 | #include <iomanip> |
| 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(Range&: Container, P: [=](const Triple &CT) { |
| 37 | return std::forward_as_tuple(args: CT.getArch(), args: CT.getOS(), |
| 38 | args: CT.getEnvironment()) < |
| 39 | std::forward_as_tuple(args: T.getArch(), args: T.getOS(), args: T.getEnvironment()); |
| 40 | }); |
| 41 | |
| 42 | if (I != Container.end() && *I == T) |
| 43 | return I; |
| 44 | return Container.emplace(position: I, args&: 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(L: cmd); |
| 58 | return getOSVersionStr(Vers.version); |
| 59 | }; |
| 60 | |
| 61 | TripleVec Triples; |
| 62 | bool IsIntel = ArchitectureSet(ArchT).hasX86(); |
| 63 | auto Arch = getArchitectureName(Arch: 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(Container&: Triples, T: {Arch, "apple" , "macos" + OSVersion}); |
| 71 | break; |
| 72 | case MachO::LC_VERSION_MIN_IPHONEOS: |
| 73 | OSVersion = getOSVersion(cmd); |
| 74 | if (IsIntel) |
| 75 | emplace(Container&: Triples, T: {Arch, "apple" , "ios" + OSVersion, "simulator" }); |
| 76 | else |
| 77 | emplace(Container&: Triples, T: {Arch, "apple" , "ios" + OSVersion}); |
| 78 | break; |
| 79 | case MachO::LC_VERSION_MIN_TVOS: |
| 80 | OSVersion = getOSVersion(cmd); |
| 81 | if (IsIntel) |
| 82 | emplace(Container&: Triples, T: {Arch, "apple" , "tvos" + OSVersion, "simulator" }); |
| 83 | else |
| 84 | emplace(Container&: Triples, T: {Arch, "apple" , "tvos" + OSVersion}); |
| 85 | break; |
| 86 | case MachO::LC_VERSION_MIN_WATCHOS: |
| 87 | OSVersion = getOSVersion(cmd); |
| 88 | if (IsIntel) |
| 89 | emplace(Container&: Triples, T: {Arch, "apple" , "watchos" + OSVersion, "simulator" }); |
| 90 | else |
| 91 | emplace(Container&: Triples, T: {Arch, "apple" , "watchos" + OSVersion}); |
| 92 | break; |
| 93 | case MachO::LC_BUILD_VERSION: { |
| 94 | OSVersion = getOSVersionStr(Obj->getBuildVersionLoadCommand(L: cmd).minos); |
| 95 | switch (Obj->getBuildVersionLoadCommand(L: cmd).platform) { |
| 96 | case MachO::PLATFORM_MACOS: |
| 97 | emplace(Container&: Triples, T: {Arch, "apple" , "macos" + OSVersion}); |
| 98 | break; |
| 99 | case MachO::PLATFORM_IOS: |
| 100 | emplace(Container&: Triples, T: {Arch, "apple" , "ios" + OSVersion}); |
| 101 | break; |
| 102 | case MachO::PLATFORM_TVOS: |
| 103 | emplace(Container&: Triples, T: {Arch, "apple" , "tvos" + OSVersion}); |
| 104 | break; |
| 105 | case MachO::PLATFORM_WATCHOS: |
| 106 | emplace(Container&: Triples, T: {Arch, "apple" , "watchos" + OSVersion}); |
| 107 | break; |
| 108 | case MachO::PLATFORM_BRIDGEOS: |
| 109 | emplace(Container&: Triples, T: {Arch, "apple" , "bridgeos" + OSVersion}); |
| 110 | break; |
| 111 | case MachO::PLATFORM_MACCATALYST: |
| 112 | emplace(Container&: Triples, T: {Arch, "apple" , "ios" + OSVersion, "macabi" }); |
| 113 | break; |
| 114 | case MachO::PLATFORM_IOSSIMULATOR: |
| 115 | emplace(Container&: Triples, T: {Arch, "apple" , "ios" + OSVersion, "simulator" }); |
| 116 | break; |
| 117 | case MachO::PLATFORM_TVOSSIMULATOR: |
| 118 | emplace(Container&: Triples, T: {Arch, "apple" , "tvos" + OSVersion, "simulator" }); |
| 119 | break; |
| 120 | case MachO::PLATFORM_WATCHOSSIMULATOR: |
| 121 | emplace(Container&: Triples, T: {Arch, "apple" , "watchos" + OSVersion, "simulator" }); |
| 122 | break; |
| 123 | case MachO::PLATFORM_DRIVERKIT: |
| 124 | emplace(Container&: Triples, T: {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(Container&: Triples, T: {Arch, "apple" , "unknown" }); |
| 140 | |
| 141 | return Triples; |
| 142 | } |
| 143 | |
| 144 | static Error (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(L: LCI); |
| 171 | BA.InstallName = Slice.copyString(String: 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(L: LCI); |
| 178 | BA.RexportedLibraries.emplace_back( |
| 179 | args: Slice.copyString(String: LCI.Ptr + DLLC.dylib.name)); |
| 180 | break; |
| 181 | } |
| 182 | case MachO::LC_SUB_FRAMEWORK: { |
| 183 | auto SFC = Obj->getSubFrameworkCommand(L: LCI); |
| 184 | BA.ParentUmbrella = Slice.copyString(String: LCI.Ptr + SFC.umbrella); |
| 185 | break; |
| 186 | } |
| 187 | case MachO::LC_SUB_CLIENT: { |
| 188 | auto SCLC = Obj->getSubClientCommand(L: LCI); |
| 189 | BA.AllowableClients.emplace_back(args: Slice.copyString(String: LCI.Ptr + SCLC.client)); |
| 190 | break; |
| 191 | } |
| 192 | case MachO::LC_UUID: { |
| 193 | auto UUIDLC = Obj->getUuidCommand(L: 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(String: Stream.str()); |
| 202 | break; |
| 203 | } |
| 204 | case MachO::LC_RPATH: { |
| 205 | auto RPLC = Obj->getRpathCommand(L: LCI); |
| 206 | BA.RPaths.emplace_back(args: Slice.copyString(String: LCI.Ptr + RPLC.path)); |
| 207 | break; |
| 208 | } |
| 209 | case MachO::LC_SEGMENT_SPLIT_INFO: { |
| 210 | auto SSILC = Obj->getLinkeditDataLoadCommand(L: 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(Name: Sym.name(), Flags, GV: 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 (Flags & SymbolRef::SF_Undefined) { |
| 297 | if (Opt.Undefineds) |
| 298 | Linkage = RecordLinkage::Undefined; |
| 299 | else |
| 300 | continue; |
| 301 | if (Flags & SymbolRef::SF_Weak) |
| 302 | RecordFlags |= SymbolFlags::WeakReferenced; |
| 303 | } else if (Flags & SymbolRef::SF_Exported) { |
| 304 | auto Exp = Exports.find(Key: Name); |
| 305 | // This should never be possible when binaries are produced with Apple |
| 306 | // linkers. However it is possible to craft dylibs where the export trie |
| 307 | // is either malformed or has conflicting symbols compared to n_list. |
| 308 | if (Exp != Exports.end()) |
| 309 | std::tie(args&: RecordFlags, args&: Linkage) = Exp->second; |
| 310 | else |
| 311 | Linkage = RecordLinkage::Exported; |
| 312 | } else if (Flags & SymbolRef::SF_Hidden) { |
| 313 | Linkage = RecordLinkage::Internal; |
| 314 | } else |
| 315 | continue; |
| 316 | |
| 317 | auto TypeOrErr = Sym.getType(); |
| 318 | if (!TypeOrErr) |
| 319 | return TypeOrErr.takeError(); |
| 320 | auto Type = *TypeOrErr; |
| 321 | |
| 322 | GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function) |
| 323 | ? GlobalRecord::Kind::Function |
| 324 | : GlobalRecord::Kind::Variable; |
| 325 | |
| 326 | if (GV == GlobalRecord::Kind::Function) |
| 327 | RecordFlags |= SymbolFlags::Text; |
| 328 | else |
| 329 | RecordFlags |= SymbolFlags::Data; |
| 330 | |
| 331 | Slice.addRecord(Name, Flags: RecordFlags, GV, Linkage); |
| 332 | } |
| 333 | return Err; |
| 334 | } |
| 335 | |
| 336 | static Error load(MachOObjectFile *Obj, RecordsSlice &Slice, |
| 337 | const ParseOption &Opt, const Architecture Arch) { |
| 338 | if (Arch == AK_unknown) |
| 339 | return make_error<TextAPIError>(Args: TextAPIErrorCode::UnsupportedTarget); |
| 340 | |
| 341 | if (Opt.MachOHeader) |
| 342 | if (auto Err = readMachOHeader(Obj, Slice)) |
| 343 | return Err; |
| 344 | |
| 345 | if (Opt.SymbolTable) |
| 346 | if (auto Err = readSymbols(Obj, Slice, Opt)) |
| 347 | return Err; |
| 348 | |
| 349 | return Error::success(); |
| 350 | } |
| 351 | |
| 352 | Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer, |
| 353 | const ParseOption &Opt) { |
| 354 | Records Results; |
| 355 | |
| 356 | auto BinOrErr = createBinary(Source: Buffer); |
| 357 | if (!BinOrErr) |
| 358 | return BinOrErr.takeError(); |
| 359 | |
| 360 | Binary &Bin = *BinOrErr.get(); |
| 361 | if (auto *Obj = dyn_cast<MachOObjectFile>(Val: &Bin)) { |
| 362 | const auto Arch = getArchitectureFromCpuType(CPUType: Obj->getHeader().cputype, |
| 363 | CPUSubType: Obj->getHeader().cpusubtype); |
| 364 | if (!Opt.Archs.has(Arch)) |
| 365 | return make_error<TextAPIError>(Args: TextAPIErrorCode::NoSuchArchitecture); |
| 366 | |
| 367 | auto Triples = constructTriples(Obj, ArchT: Arch); |
| 368 | for (const auto &T : Triples) { |
| 369 | if (mapToPlatformType(Target: T) == PLATFORM_UNKNOWN) |
| 370 | return make_error<TextAPIError>(Args: TextAPIErrorCode::UnsupportedTarget); |
| 371 | Results.emplace_back(Args: std::make_shared<RecordsSlice>(args: RecordsSlice({T}))); |
| 372 | if (auto Err = load(Obj, Slice&: *Results.back(), Opt, Arch)) |
| 373 | return std::move(Err); |
| 374 | Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier(); |
| 375 | } |
| 376 | return Results; |
| 377 | } |
| 378 | |
| 379 | // Only expect MachO universal binaries at this point. |
| 380 | assert(isa<MachOUniversalBinary>(&Bin) && |
| 381 | "Expected a MachO universal binary." ); |
| 382 | auto *UB = cast<MachOUniversalBinary>(Val: &Bin); |
| 383 | |
| 384 | for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) { |
| 385 | // Skip architecture if not requested. |
| 386 | auto Arch = |
| 387 | getArchitectureFromCpuType(CPUType: OI->getCPUType(), CPUSubType: OI->getCPUSubType()); |
| 388 | if (!Opt.Archs.has(Arch)) |
| 389 | continue; |
| 390 | |
| 391 | // Skip unknown architectures. |
| 392 | if (Arch == AK_unknown) |
| 393 | continue; |
| 394 | |
| 395 | // This can fail if the object is an archive. |
| 396 | auto ObjOrErr = OI->getAsObjectFile(); |
| 397 | |
| 398 | // Skip the archive and consume the error. |
| 399 | if (!ObjOrErr) { |
| 400 | consumeError(Err: ObjOrErr.takeError()); |
| 401 | continue; |
| 402 | } |
| 403 | |
| 404 | auto &Obj = *ObjOrErr.get(); |
| 405 | switch (Obj.getHeader().filetype) { |
| 406 | default: |
| 407 | break; |
| 408 | case MachO::MH_BUNDLE: |
| 409 | case MachO::MH_DYLIB: |
| 410 | case MachO::MH_DYLIB_STUB: |
| 411 | for (const auto &T : constructTriples(Obj: &Obj, ArchT: Arch)) { |
| 412 | Results.emplace_back(Args: std::make_shared<RecordsSlice>(args: RecordsSlice({T}))); |
| 413 | if (auto Err = load(Obj: &Obj, Slice&: *Results.back(), Opt, Arch)) |
| 414 | return std::move(Err); |
| 415 | Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier(); |
| 416 | } |
| 417 | break; |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | if (Results.empty()) |
| 422 | return make_error<TextAPIError>(Args: TextAPIErrorCode::EmptyResults); |
| 423 | return Results; |
| 424 | } |
| 425 | |
| 426 | Expected<std::unique_ptr<InterfaceFile>> |
| 427 | DylibReader::get(MemoryBufferRef Buffer) { |
| 428 | ParseOption Options; |
| 429 | auto SlicesOrErr = readFile(Buffer, Opt: Options); |
| 430 | if (!SlicesOrErr) |
| 431 | return SlicesOrErr.takeError(); |
| 432 | |
| 433 | return convertToInterfaceFile(Slices: *SlicesOrErr); |
| 434 | } |
| 435 | |
| 436 | static void DWARFErrorHandler(Error Err) { /**/ } |
| 437 | |
| 438 | static SymbolToSourceLocMap |
| 439 | accumulateLocs(MachOObjectFile &Obj, |
| 440 | const std::unique_ptr<DWARFContext> &DiCtx) { |
| 441 | SymbolToSourceLocMap LocMap; |
| 442 | for (const auto &Symbol : Obj.symbols()) { |
| 443 | Expected<uint32_t> FlagsOrErr = Symbol.getFlags(); |
| 444 | if (!FlagsOrErr) { |
| 445 | consumeError(Err: FlagsOrErr.takeError()); |
| 446 | continue; |
| 447 | } |
| 448 | |
| 449 | if (!(*FlagsOrErr & SymbolRef::SF_Exported)) |
| 450 | continue; |
| 451 | |
| 452 | Expected<uint64_t> AddressOrErr = Symbol.getAddress(); |
| 453 | if (!AddressOrErr) { |
| 454 | consumeError(Err: AddressOrErr.takeError()); |
| 455 | continue; |
| 456 | } |
| 457 | const uint64_t Address = *AddressOrErr; |
| 458 | |
| 459 | auto TypeOrErr = Symbol.getType(); |
| 460 | if (!TypeOrErr) { |
| 461 | consumeError(Err: TypeOrErr.takeError()); |
| 462 | continue; |
| 463 | } |
| 464 | const bool IsCode = (*TypeOrErr & SymbolRef::ST_Function); |
| 465 | |
| 466 | auto *DWARFCU = IsCode ? DiCtx->getCompileUnitForCodeAddress(Address) |
| 467 | : DiCtx->getCompileUnitForDataAddress(Address); |
| 468 | if (!DWARFCU) |
| 469 | continue; |
| 470 | |
| 471 | const DWARFDie &DIE = IsCode ? DWARFCU->getSubroutineForAddress(Address) |
| 472 | : DWARFCU->getVariableForAddress(Address); |
| 473 | const std::string File = DIE.getDeclFile( |
| 474 | Kind: llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); |
| 475 | const uint64_t Line = DIE.getDeclLine(); |
| 476 | |
| 477 | auto NameOrErr = Symbol.getName(); |
| 478 | if (!NameOrErr) { |
| 479 | consumeError(Err: NameOrErr.takeError()); |
| 480 | continue; |
| 481 | } |
| 482 | auto Name = *NameOrErr; |
| 483 | auto Sym = parseSymbol(SymName: Name); |
| 484 | |
| 485 | if (!File.empty() && Line != 0) |
| 486 | LocMap.insert(KV: {Sym.Name, RecordLoc(File, Line)}); |
| 487 | } |
| 488 | |
| 489 | return LocMap; |
| 490 | } |
| 491 | |
| 492 | SymbolToSourceLocMap |
| 493 | DylibReader::accumulateSourceLocFromDSYM(const StringRef DSYM, |
| 494 | const Target &T) { |
| 495 | // Find sidecar file. |
| 496 | auto DSYMsOrErr = MachOObjectFile::findDsymObjectMembers(Path: DSYM); |
| 497 | if (!DSYMsOrErr) { |
| 498 | consumeError(Err: DSYMsOrErr.takeError()); |
| 499 | return SymbolToSourceLocMap(); |
| 500 | } |
| 501 | if (DSYMsOrErr->empty()) |
| 502 | return SymbolToSourceLocMap(); |
| 503 | |
| 504 | const StringRef Path = DSYMsOrErr->front(); |
| 505 | auto BufOrErr = MemoryBuffer::getFile(Filename: Path); |
| 506 | if (auto Err = BufOrErr.getError()) |
| 507 | return SymbolToSourceLocMap(); |
| 508 | |
| 509 | auto BinOrErr = createBinary(Source: *BufOrErr.get()); |
| 510 | if (!BinOrErr) { |
| 511 | consumeError(Err: BinOrErr.takeError()); |
| 512 | return SymbolToSourceLocMap(); |
| 513 | } |
| 514 | // Handle single arch. |
| 515 | if (auto *Single = dyn_cast<MachOObjectFile>(Val: BinOrErr->get())) { |
| 516 | auto DiCtx = DWARFContext::create( |
| 517 | Obj: *Single, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: "" , |
| 518 | RecoverableErrorHandler: DWARFErrorHandler, WarningHandler: DWARFErrorHandler); |
| 519 | |
| 520 | return accumulateLocs(Obj&: *Single, DiCtx); |
| 521 | } |
| 522 | // Handle universal companion file. |
| 523 | if (auto *Fat = dyn_cast<MachOUniversalBinary>(Val: BinOrErr->get())) { |
| 524 | auto ObjForArch = Fat->getObjectForArch(ArchName: getArchitectureName(Arch: T.Arch)); |
| 525 | if (!ObjForArch) { |
| 526 | consumeError(Err: ObjForArch.takeError()); |
| 527 | return SymbolToSourceLocMap(); |
| 528 | } |
| 529 | auto MachOOrErr = ObjForArch->getAsObjectFile(); |
| 530 | if (!MachOOrErr) { |
| 531 | consumeError(Err: MachOOrErr.takeError()); |
| 532 | return SymbolToSourceLocMap(); |
| 533 | } |
| 534 | auto &Obj = **MachOOrErr; |
| 535 | auto DiCtx = DWARFContext::create( |
| 536 | Obj, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: "" , |
| 537 | RecoverableErrorHandler: DWARFErrorHandler, WarningHandler: DWARFErrorHandler); |
| 538 | |
| 539 | return accumulateLocs(Obj, DiCtx); |
| 540 | } |
| 541 | return SymbolToSourceLocMap(); |
| 542 | } |
| 543 | |