1 //===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===// 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 #include "BinaryHolder.h" 10 #include "DebugMap.h" 11 #include "MachOUtils.h" 12 #include "llvm/ADT/DenseSet.h" 13 #include "llvm/ADT/SmallSet.h" 14 #include "llvm/Object/MachO.h" 15 #include "llvm/Support/Chrono.h" 16 #include "llvm/Support/Path.h" 17 #include "llvm/Support/WithColor.h" 18 #include "llvm/Support/raw_ostream.h" 19 #include <optional> 20 #include <vector> 21 22 namespace { 23 using namespace llvm; 24 using namespace llvm::dsymutil; 25 using namespace llvm::object; 26 27 class MachODebugMapParser { 28 public: 29 MachODebugMapParser(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, 30 StringRef BinaryPath, ArrayRef<std::string> Archs, 31 StringRef PathPrefix = "", bool Verbose = false) 32 : BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()), 33 PathPrefix(std::string(PathPrefix)), BinHolder(VFS, Verbose), 34 CurrentDebugMapObject(nullptr), SkipDebugMapObject(false) {} 35 36 /// Parses and returns the DebugMaps of the input binary. The binary contains 37 /// multiple maps in case it is a universal binary. 38 /// \returns an error in case the provided BinaryPath doesn't exist 39 /// or isn't of a supported type. 40 ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse(); 41 42 /// Walk the symbol table and dump it. 43 bool dumpStab(); 44 45 using OSO = std::pair<llvm::StringRef, uint64_t>; 46 47 private: 48 std::string BinaryPath; 49 SmallVector<StringRef, 1> Archs; 50 std::string PathPrefix; 51 52 /// Owns the MemoryBuffer for the main binary. 53 BinaryHolder BinHolder; 54 /// Map of the binary symbol addresses. 55 StringMap<uint64_t> MainBinarySymbolAddresses; 56 StringRef MainBinaryStrings; 57 /// The constructed DebugMap. 58 std::unique_ptr<DebugMap> Result; 59 /// List of common symbols that need to be added to the debug map. 60 std::vector<std::string> CommonSymbols; 61 62 /// Map of the currently processed object file symbol addresses. 63 StringMap<std::optional<uint64_t>> CurrentObjectAddresses; 64 65 /// Lazily computed map of symbols aliased to the processed object file. 66 StringMap<std::optional<uint64_t>> CurrentObjectAliasMap; 67 68 /// If CurrentObjectAliasMap has been computed for a given address. 69 SmallSet<uint64_t, 4> SeenAliasValues; 70 71 /// Element of the debug map corresponding to the current object file. 72 DebugMapObject *CurrentDebugMapObject; 73 74 /// Whether we need to skip the current debug map object. 75 bool SkipDebugMapObject; 76 77 /// Holds function info while function scope processing. 78 const char *CurrentFunctionName; 79 uint64_t CurrentFunctionAddress; 80 81 std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary, 82 StringRef BinaryPath); 83 void handleStabDebugMap( 84 const MachOObjectFile &MainBinary, 85 std::function<void(uint32_t, uint8_t, uint8_t, uint16_t, uint64_t)> F); 86 87 void 88 switchToNewDebugMapObject(StringRef Filename, 89 sys::TimePoint<std::chrono::seconds> Timestamp); 90 void resetParserState(); 91 uint64_t getMainBinarySymbolAddress(StringRef Name); 92 std::vector<StringRef> getMainBinarySymbolNames(uint64_t Value); 93 void loadMainBinarySymbols(const MachOObjectFile &MainBinary); 94 void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj); 95 96 void handleStabOSOEntry(uint32_t StringIndex, uint8_t Type, 97 uint8_t SectionIndex, uint16_t Flags, uint64_t Value, 98 llvm::DenseSet<OSO> &OSOs, 99 llvm::SmallSet<OSO, 4> &Duplicates); 100 void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, 101 uint8_t SectionIndex, uint16_t Flags, 102 uint64_t Value, 103 const llvm::SmallSet<OSO, 4> &Duplicates); 104 105 template <typename STEType> 106 void handleStabDebugMapEntry( 107 const STEType &STE, 108 std::function<void(uint32_t, uint8_t, uint8_t, uint16_t, uint64_t)> F) { 109 F(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, STE.n_value); 110 } 111 112 void addCommonSymbols(); 113 114 /// Dump the symbol table output header. 115 void dumpSymTabHeader(raw_ostream &OS, StringRef Arch); 116 117 /// Dump the contents of nlist entries. 118 void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex, 119 uint8_t Type, uint8_t SectionIndex, uint16_t Flags, 120 uint64_t Value); 121 122 template <typename STEType> 123 void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) { 124 dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, 125 STE.n_value); 126 } 127 void dumpOneBinaryStab(const MachOObjectFile &MainBinary, 128 StringRef BinaryPath); 129 130 void Warning(const Twine &Msg, StringRef File = StringRef()) { 131 assert(Result && 132 "The debug map must be initialized before calling this function"); 133 WithColor::warning() << "(" 134 << MachOUtils::getArchName( 135 Result->getTriple().getArchName()) 136 << ") " << File << " " << Msg << "\n"; 137 } 138 }; 139 140 } // anonymous namespace 141 142 /// Reset the parser state corresponding to the current object 143 /// file. This is to be called after an object file is finished 144 /// processing. 145 void MachODebugMapParser::resetParserState() { 146 CommonSymbols.clear(); 147 CurrentObjectAddresses.clear(); 148 CurrentObjectAliasMap.clear(); 149 SeenAliasValues.clear(); 150 CurrentDebugMapObject = nullptr; 151 SkipDebugMapObject = false; 152 } 153 154 /// Commons symbols won't show up in the symbol map but might need to be 155 /// relocated. We can add them to the symbol table ourselves by combining the 156 /// information in the object file (the symbol name) and the main binary (the 157 /// address). 158 void MachODebugMapParser::addCommonSymbols() { 159 for (auto &CommonSymbol : CommonSymbols) { 160 uint64_t CommonAddr = getMainBinarySymbolAddress(CommonSymbol); 161 if (CommonAddr == 0) { 162 // The main binary doesn't have an address for the given symbol. 163 continue; 164 } 165 if (!CurrentDebugMapObject->addSymbol(CommonSymbol, 166 std::nullopt /*ObjectAddress*/, 167 CommonAddr, 0 /*size*/)) { 168 // The symbol is already present. 169 continue; 170 } 171 } 172 } 173 174 /// Create a new DebugMapObject. This function resets the state of the 175 /// parser that was referring to the last object file and sets 176 /// everything up to add symbols to the new one. 177 void MachODebugMapParser::switchToNewDebugMapObject( 178 StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { 179 addCommonSymbols(); 180 resetParserState(); 181 182 SmallString<80> Path(PathPrefix); 183 sys::path::append(Path, Filename); 184 185 auto ObjectEntry = BinHolder.getObjectEntry(Path, Timestamp); 186 if (!ObjectEntry) { 187 auto Err = ObjectEntry.takeError(); 188 Warning("unable to open object file: " + toString(std::move(Err)), 189 Path.str()); 190 return; 191 } 192 193 auto Object = ObjectEntry->getObjectAs<MachOObjectFile>(Result->getTriple()); 194 if (!Object) { 195 auto Err = Object.takeError(); 196 Warning("unable to open object file: " + toString(std::move(Err)), 197 Path.str()); 198 return; 199 } 200 201 CurrentDebugMapObject = 202 &Result->addDebugMapObject(Path, Timestamp, MachO::N_OSO); 203 loadCurrentObjectFileSymbols(*Object); 204 } 205 206 static std::string getArchName(const object::MachOObjectFile &Obj) { 207 Triple T = Obj.getArchTriple(); 208 return std::string(T.getArchName()); 209 } 210 211 void MachODebugMapParser::handleStabDebugMap( 212 const MachOObjectFile &MainBinary, 213 std::function<void(uint32_t, uint8_t, uint8_t, uint16_t, uint64_t)> F) { 214 for (const SymbolRef &Symbol : MainBinary.symbols()) { 215 const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); 216 if (MainBinary.is64Bit()) 217 handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI), F); 218 else 219 handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI), F); 220 } 221 } 222 223 std::unique_ptr<DebugMap> 224 MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, 225 StringRef BinaryPath) { 226 Result = std::make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath, 227 MainBinary.getUuid()); 228 loadMainBinarySymbols(MainBinary); 229 MainBinaryStrings = MainBinary.getStringTableData(); 230 231 // Static archives can contain multiple object files with identical names, in 232 // which case the timestamp is used to disambiguate. However, if both are 233 // identical, there's no way to tell them apart. Detect this and skip 234 // duplicate debug map objects. 235 llvm::DenseSet<OSO> OSOs; 236 llvm::SmallSet<OSO, 4> Duplicates; 237 238 // Iterate over all the STABS to find duplicate OSO entries. 239 handleStabDebugMap(MainBinary, 240 [&](uint32_t StringIndex, uint8_t Type, 241 uint8_t SectionIndex, uint16_t Flags, uint64_t Value) { 242 handleStabOSOEntry(StringIndex, Type, SectionIndex, 243 Flags, Value, OSOs, Duplicates); 244 }); 245 246 // Print an informative warning with the duplicate object file name and time 247 // stamp. 248 for (const auto &OSO : Duplicates) { 249 std::string Buffer; 250 llvm::raw_string_ostream OS(Buffer); 251 OS << sys::TimePoint<std::chrono::seconds>(sys::toTimePoint(OSO.second)); 252 Warning("skipping debug map object with duplicate name and timestamp: " + 253 OS.str() + Twine(" ") + Twine(OSO.first)); 254 } 255 256 // Build the debug map by iterating over the STABS again but ignore the 257 // duplicate debug objects. 258 handleStabDebugMap(MainBinary, [&](uint32_t StringIndex, uint8_t Type, 259 uint8_t SectionIndex, uint16_t Flags, 260 uint64_t Value) { 261 handleStabSymbolTableEntry(StringIndex, Type, SectionIndex, Flags, Value, 262 Duplicates); 263 }); 264 265 resetParserState(); 266 return std::move(Result); 267 } 268 269 // Table that maps Darwin's Mach-O stab constants to strings to allow printing. 270 // llvm-nm has very similar code, the strings used here are however slightly 271 // different and part of the interface of dsymutil (some project's build-systems 272 // parse the ouptut of dsymutil -s), thus they shouldn't be changed. 273 struct DarwinStabName { 274 uint8_t NType; 275 const char *Name; 276 }; 277 278 const struct DarwinStabName DarwinStabNames[] = { 279 {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"}, 280 {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"}, 281 {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"}, 282 {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"}, 283 {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"}, 284 {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"}, 285 {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"}, 286 {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"}, 287 {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"}, 288 {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"}, 289 {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"}, 290 {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"}, 291 {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"}, 292 {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"}, 293 {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"}, 294 {MachO::N_LENG, "N_LENG"}, {0, nullptr}}; 295 296 static const char *getDarwinStabString(uint8_t NType) { 297 for (unsigned i = 0; DarwinStabNames[i].Name; i++) { 298 if (DarwinStabNames[i].NType == NType) 299 return DarwinStabNames[i].Name; 300 } 301 return nullptr; 302 } 303 304 void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) { 305 OS << "-----------------------------------" 306 "-----------------------------------\n"; 307 OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n"; 308 OS << "-----------------------------------" 309 "-----------------------------------\n"; 310 OS << "Index n_strx n_type n_sect n_desc n_value\n"; 311 OS << "======== -------- ------------------ ------ ------ ----------------\n"; 312 } 313 314 void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, 315 uint32_t StringIndex, uint8_t Type, 316 uint8_t SectionIndex, uint16_t Flags, 317 uint64_t Value) { 318 // Index 319 OS << '[' << format_decimal(Index, 6) 320 << "] " 321 // n_strx 322 << format_hex_no_prefix(StringIndex, 8) 323 << ' ' 324 // n_type... 325 << format_hex_no_prefix(Type, 2) << " ("; 326 327 if (Type & MachO::N_STAB) 328 OS << left_justify(getDarwinStabString(Type), 13); 329 else { 330 if (Type & MachO::N_PEXT) 331 OS << "PEXT "; 332 else 333 OS << " "; 334 switch (Type & MachO::N_TYPE) { 335 case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT 336 OS << "UNDF"; 337 break; 338 case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT 339 OS << "ABS "; 340 break; 341 case MachO::N_SECT: // 0xe defined in section number n_sect 342 OS << "SECT"; 343 break; 344 case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib) 345 OS << "PBUD"; 346 break; 347 case MachO::N_INDR: // 0xa indirect 348 OS << "INDR"; 349 break; 350 default: 351 OS << format_hex_no_prefix(Type, 2) << " "; 352 break; 353 } 354 if (Type & MachO::N_EXT) 355 OS << " EXT"; 356 else 357 OS << " "; 358 } 359 360 OS << ") " 361 // n_sect 362 << format_hex_no_prefix(SectionIndex, 2) 363 << " " 364 // n_desc 365 << format_hex_no_prefix(Flags, 4) 366 << " " 367 // n_value 368 << format_hex_no_prefix(Value, 16); 369 370 const char *Name = &MainBinaryStrings.data()[StringIndex]; 371 if (Name && Name[0]) 372 OS << " '" << Name << "'"; 373 374 OS << "\n"; 375 } 376 377 void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary, 378 StringRef BinaryPath) { 379 loadMainBinarySymbols(MainBinary); 380 MainBinaryStrings = MainBinary.getStringTableData(); 381 raw_ostream &OS(llvm::outs()); 382 383 dumpSymTabHeader(OS, getArchName(MainBinary)); 384 uint64_t Idx = 0; 385 for (const SymbolRef &Symbol : MainBinary.symbols()) { 386 const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); 387 if (MainBinary.is64Bit()) 388 dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI)); 389 else 390 dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI)); 391 Idx++; 392 } 393 394 OS << "\n\n"; 395 resetParserState(); 396 } 397 398 static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) { 399 if (Archs.empty() || is_contained(Archs, "all") || is_contained(Archs, "*")) 400 return true; 401 402 if (Arch.startswith("arm") && Arch != "arm64" && is_contained(Archs, "arm")) 403 return true; 404 405 SmallString<16> ArchName = Arch; 406 if (Arch.startswith("thumb")) 407 ArchName = ("arm" + Arch.substr(5)).str(); 408 409 return is_contained(Archs, ArchName); 410 } 411 412 bool MachODebugMapParser::dumpStab() { 413 auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); 414 if (!ObjectEntry) { 415 auto Err = ObjectEntry.takeError(); 416 WithColor::error() << "cannot load '" << BinaryPath 417 << "': " << toString(std::move(Err)) << '\n'; 418 return false; 419 } 420 421 auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); 422 if (!Objects) { 423 auto Err = Objects.takeError(); 424 WithColor::error() << "cannot get '" << BinaryPath 425 << "' as MachO file: " << toString(std::move(Err)) 426 << "\n"; 427 return false; 428 } 429 430 for (const auto *Object : *Objects) 431 if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) 432 dumpOneBinaryStab(*Object, BinaryPath); 433 434 return true; 435 } 436 437 /// This main parsing routine tries to open the main binary and if 438 /// successful iterates over the STAB entries. The real parsing is 439 /// done in handleStabSymbolTableEntry. 440 ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() { 441 auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); 442 if (!ObjectEntry) { 443 return errorToErrorCode(ObjectEntry.takeError()); 444 } 445 446 auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); 447 if (!Objects) { 448 return errorToErrorCode(Objects.takeError()); 449 } 450 451 std::vector<std::unique_ptr<DebugMap>> Results; 452 for (const auto *Object : *Objects) 453 if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) 454 Results.push_back(parseOneBinary(*Object, BinaryPath)); 455 456 return std::move(Results); 457 } 458 459 void MachODebugMapParser::handleStabOSOEntry( 460 uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags, 461 uint64_t Value, llvm::DenseSet<OSO> &OSOs, 462 llvm::SmallSet<OSO, 4> &Duplicates) { 463 if (Type != MachO::N_OSO) 464 return; 465 466 OSO O(&MainBinaryStrings.data()[StringIndex], Value); 467 if (!OSOs.insert(O).second) 468 Duplicates.insert(O); 469 } 470 471 /// Interpret the STAB entries to fill the DebugMap. 472 void MachODebugMapParser::handleStabSymbolTableEntry( 473 uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags, 474 uint64_t Value, const llvm::SmallSet<OSO, 4> &Duplicates) { 475 if (!(Type & MachO::N_STAB)) 476 return; 477 478 const char *Name = &MainBinaryStrings.data()[StringIndex]; 479 480 // An N_OSO entry represents the start of a new object file description. 481 if (Type == MachO::N_OSO) { 482 if (Duplicates.count(OSO(Name, Value))) { 483 SkipDebugMapObject = true; 484 return; 485 } 486 return switchToNewDebugMapObject(Name, sys::toTimePoint(Value)); 487 } 488 489 if (SkipDebugMapObject) 490 return; 491 492 if (Type == MachO::N_AST) { 493 SmallString<80> Path(PathPrefix); 494 sys::path::append(Path, Name); 495 Result->addDebugMapObject(Path, sys::toTimePoint(Value), Type); 496 return; 497 } 498 499 // If the last N_OSO object file wasn't found, CurrentDebugMapObject will be 500 // null. Do not update anything until we find the next valid N_OSO entry. 501 if (!CurrentDebugMapObject) 502 return; 503 504 uint32_t Size = 0; 505 switch (Type) { 506 case MachO::N_GSYM: 507 // This is a global variable. We need to query the main binary 508 // symbol table to find its address as it might not be in the 509 // debug map (for common symbols). 510 Value = getMainBinarySymbolAddress(Name); 511 break; 512 case MachO::N_FUN: 513 // Functions are scopes in STABS. They have an end marker that 514 // contains the function size. 515 if (Name[0] == '\0') { 516 Size = Value; 517 Value = CurrentFunctionAddress; 518 Name = CurrentFunctionName; 519 break; 520 } else { 521 CurrentFunctionName = Name; 522 CurrentFunctionAddress = Value; 523 return; 524 } 525 case MachO::N_STSYM: 526 break; 527 default: 528 return; 529 } 530 531 auto ObjectSymIt = CurrentObjectAddresses.find(Name); 532 533 // If the name of a (non-static) symbol is not in the current object, we 534 // check all its aliases from the main binary. 535 if (ObjectSymIt == CurrentObjectAddresses.end() && Type != MachO::N_STSYM) { 536 if (SeenAliasValues.count(Value) == 0) { 537 auto Aliases = getMainBinarySymbolNames(Value); 538 for (const auto &Alias : Aliases) { 539 auto It = CurrentObjectAddresses.find(Alias); 540 if (It != CurrentObjectAddresses.end()) { 541 auto AliasValue = It->getValue(); 542 for (const auto &Alias : Aliases) 543 CurrentObjectAliasMap[Alias] = AliasValue; 544 break; 545 } 546 } 547 SeenAliasValues.insert(Value); 548 } 549 550 auto AliasIt = CurrentObjectAliasMap.find(Name); 551 if (AliasIt != CurrentObjectAliasMap.end()) 552 ObjectSymIt = AliasIt; 553 } 554 555 // ThinLTO adds a unique suffix to exported private symbols. 556 if (ObjectSymIt == CurrentObjectAddresses.end()) { 557 for (auto Iter = CurrentObjectAddresses.begin(); 558 Iter != CurrentObjectAddresses.end(); ++Iter) { 559 llvm::StringRef SymbolName = Iter->getKey(); 560 auto Pos = SymbolName.rfind(".llvm."); 561 if (Pos != llvm::StringRef::npos && SymbolName.substr(0, Pos) == Name) { 562 ObjectSymIt = Iter; 563 break; 564 } 565 } 566 } 567 568 if (ObjectSymIt == CurrentObjectAddresses.end()) { 569 Warning("could not find object file symbol for symbol " + Twine(Name)); 570 return; 571 } 572 573 if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value, 574 Size)) { 575 Warning(Twine("failed to insert symbol '") + Name + "' in the debug map."); 576 return; 577 } 578 } 579 580 /// Load the current object file symbols into CurrentObjectAddresses. 581 void MachODebugMapParser::loadCurrentObjectFileSymbols( 582 const object::MachOObjectFile &Obj) { 583 CurrentObjectAddresses.clear(); 584 585 for (auto Sym : Obj.symbols()) { 586 uint64_t Addr = cantFail(Sym.getValue()); 587 Expected<StringRef> Name = Sym.getName(); 588 if (!Name) { 589 auto Err = Name.takeError(); 590 Warning("failed to get symbol name: " + toString(std::move(Err)), 591 Obj.getFileName()); 592 continue; 593 } 594 // The value of some categories of symbols isn't meaningful. For 595 // example common symbols store their size in the value field, not 596 // their address. Absolute symbols have a fixed address that can 597 // conflict with standard symbols. These symbols (especially the 598 // common ones), might still be referenced by relocations. These 599 // relocations will use the symbol itself, and won't need an 600 // object file address. The object file address field is optional 601 // in the DebugMap, leave it unassigned for these symbols. 602 uint32_t Flags = cantFail(Sym.getFlags()); 603 if (Flags & SymbolRef::SF_Absolute) { 604 CurrentObjectAddresses[*Name] = std::nullopt; 605 } else if (Flags & SymbolRef::SF_Common) { 606 CurrentObjectAddresses[*Name] = std::nullopt; 607 CommonSymbols.push_back(std::string(*Name)); 608 } else { 609 CurrentObjectAddresses[*Name] = Addr; 610 } 611 } 612 } 613 614 /// Lookup a symbol address in the main binary symbol table. The 615 /// parser only needs to query common symbols, thus not every symbol's 616 /// address is available through this function. 617 uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) { 618 auto Sym = MainBinarySymbolAddresses.find(Name); 619 if (Sym == MainBinarySymbolAddresses.end()) 620 return 0; 621 return Sym->second; 622 } 623 624 /// Get all symbol names in the main binary for the given value. 625 std::vector<StringRef> 626 MachODebugMapParser::getMainBinarySymbolNames(uint64_t Value) { 627 std::vector<StringRef> Names; 628 for (const auto &Entry : MainBinarySymbolAddresses) { 629 if (Entry.second == Value) 630 Names.push_back(Entry.first()); 631 } 632 return Names; 633 } 634 635 /// Load the interesting main binary symbols' addresses into 636 /// MainBinarySymbolAddresses. 637 void MachODebugMapParser::loadMainBinarySymbols( 638 const MachOObjectFile &MainBinary) { 639 section_iterator Section = MainBinary.section_end(); 640 MainBinarySymbolAddresses.clear(); 641 for (const auto &Sym : MainBinary.symbols()) { 642 Expected<SymbolRef::Type> TypeOrErr = Sym.getType(); 643 if (!TypeOrErr) { 644 auto Err = TypeOrErr.takeError(); 645 Warning("failed to get symbol type: " + toString(std::move(Err)), 646 MainBinary.getFileName()); 647 continue; 648 } 649 SymbolRef::Type Type = *TypeOrErr; 650 // Skip undefined and STAB entries. 651 if ((Type == SymbolRef::ST_Debug) || (Type == SymbolRef::ST_Unknown)) 652 continue; 653 // In theory, the only symbols of interest are the global variables. These 654 // are the only ones that need to be queried because the address of common 655 // data won't be described in the debug map. All other addresses should be 656 // fetched for the debug map. In reality, by playing with 'ld -r' and 657 // export lists, you can get symbols described as N_GSYM in the debug map, 658 // but associated with a local symbol. Gather all the symbols, but prefer 659 // the global ones. 660 uint8_t SymType = 661 MainBinary.getSymbolTableEntry(Sym.getRawDataRefImpl()).n_type; 662 bool Extern = SymType & (MachO::N_EXT | MachO::N_PEXT); 663 Expected<section_iterator> SectionOrErr = Sym.getSection(); 664 if (!SectionOrErr) { 665 auto Err = TypeOrErr.takeError(); 666 Warning("failed to get symbol section: " + toString(std::move(Err)), 667 MainBinary.getFileName()); 668 continue; 669 } 670 Section = *SectionOrErr; 671 if ((Section == MainBinary.section_end() || Section->isText()) && !Extern) 672 continue; 673 uint64_t Addr = cantFail(Sym.getValue()); 674 Expected<StringRef> NameOrErr = Sym.getName(); 675 if (!NameOrErr) { 676 auto Err = NameOrErr.takeError(); 677 Warning("failed to get symbol name: " + toString(std::move(Err)), 678 MainBinary.getFileName()); 679 continue; 680 } 681 StringRef Name = *NameOrErr; 682 if (Name.size() == 0 || Name[0] == '\0') 683 continue; 684 // Override only if the new key is global. 685 if (Extern) 686 MainBinarySymbolAddresses[Name] = Addr; 687 else 688 MainBinarySymbolAddresses.try_emplace(Name, Addr); 689 } 690 } 691 692 namespace llvm { 693 namespace dsymutil { 694 llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>> 695 parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, 696 StringRef InputFile, ArrayRef<std::string> Archs, 697 StringRef PrependPath, bool Verbose, bool InputIsYAML) { 698 if (InputIsYAML) 699 return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); 700 701 MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, Verbose); 702 return Parser.parse(); 703 } 704 705 bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, 706 StringRef InputFile, ArrayRef<std::string> Archs, 707 StringRef PrependPath) { 708 MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, false); 709 return Parser.dumpStab(); 710 } 711 } // namespace dsymutil 712 } // namespace llvm 713