1 //===-- LLVMSymbolize.cpp -------------------------------------------------===// 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 // Implementation for LLVM symbolization library. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/DebugInfo/Symbolize/Symbolize.h" 14 15 #include "SymbolizableObjectFile.h" 16 17 #include "llvm/ADT/STLExtras.h" 18 #include "llvm/BinaryFormat/COFF.h" 19 #include "llvm/DebugInfo/DWARF/DWARFContext.h" 20 #include "llvm/DebugInfo/PDB/PDB.h" 21 #include "llvm/DebugInfo/PDB/PDBContext.h" 22 #include "llvm/Demangle/Demangle.h" 23 #include "llvm/Object/COFF.h" 24 #include "llvm/Object/MachO.h" 25 #include "llvm/Object/MachOUniversal.h" 26 #include "llvm/Support/CRC.h" 27 #include "llvm/Support/Casting.h" 28 #include "llvm/Support/Compression.h" 29 #include "llvm/Support/DataExtractor.h" 30 #include "llvm/Support/Errc.h" 31 #include "llvm/Support/FileSystem.h" 32 #include "llvm/Support/MemoryBuffer.h" 33 #include "llvm/Support/Path.h" 34 #include <algorithm> 35 #include <cassert> 36 #include <cstring> 37 38 namespace llvm { 39 namespace symbolize { 40 41 Expected<DILineInfo> 42 LLVMSymbolizer::symbolizeCodeCommon(SymbolizableModule *Info, 43 object::SectionedAddress ModuleOffset) { 44 // A null module means an error has already been reported. Return an empty 45 // result. 46 if (!Info) 47 return DILineInfo(); 48 49 // If the user is giving us relative addresses, add the preferred base of the 50 // object to the offset before we do the query. It's what DIContext expects. 51 if (Opts.RelativeAddresses) 52 ModuleOffset.Address += Info->getModulePreferredBase(); 53 54 DILineInfo LineInfo = Info->symbolizeCode( 55 ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), 56 Opts.UseSymbolTable); 57 if (Opts.Demangle) 58 LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info); 59 return LineInfo; 60 } 61 62 Expected<DILineInfo> 63 LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj, 64 object::SectionedAddress ModuleOffset) { 65 StringRef ModuleName = Obj.getFileName(); 66 auto I = Modules.find(ModuleName); 67 if (I != Modules.end()) 68 return symbolizeCodeCommon(I->second.get(), ModuleOffset); 69 70 std::unique_ptr<DIContext> Context = DWARFContext::create(Obj); 71 Expected<SymbolizableModule *> InfoOrErr = 72 createModuleInfo(&Obj, std::move(Context), ModuleName); 73 if (!InfoOrErr) 74 return InfoOrErr.takeError(); 75 return symbolizeCodeCommon(*InfoOrErr, ModuleOffset); 76 } 77 78 Expected<DILineInfo> 79 LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, 80 object::SectionedAddress ModuleOffset) { 81 Expected<SymbolizableModule *> InfoOrErr = getOrCreateModuleInfo(ModuleName); 82 if (!InfoOrErr) 83 return InfoOrErr.takeError(); 84 return symbolizeCodeCommon(*InfoOrErr, ModuleOffset); 85 } 86 87 Expected<DIInliningInfo> 88 LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, 89 object::SectionedAddress ModuleOffset) { 90 SymbolizableModule *Info; 91 if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) 92 Info = InfoOrErr.get(); 93 else 94 return InfoOrErr.takeError(); 95 96 // A null module means an error has already been reported. Return an empty 97 // result. 98 if (!Info) 99 return DIInliningInfo(); 100 101 // If the user is giving us relative addresses, add the preferred base of the 102 // object to the offset before we do the query. It's what DIContext expects. 103 if (Opts.RelativeAddresses) 104 ModuleOffset.Address += Info->getModulePreferredBase(); 105 106 DIInliningInfo InlinedContext = Info->symbolizeInlinedCode( 107 ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), 108 Opts.UseSymbolTable); 109 if (Opts.Demangle) { 110 for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) { 111 auto *Frame = InlinedContext.getMutableFrame(i); 112 Frame->FunctionName = DemangleName(Frame->FunctionName, Info); 113 } 114 } 115 return InlinedContext; 116 } 117 118 Expected<DIGlobal> 119 LLVMSymbolizer::symbolizeData(const std::string &ModuleName, 120 object::SectionedAddress ModuleOffset) { 121 SymbolizableModule *Info; 122 if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) 123 Info = InfoOrErr.get(); 124 else 125 return InfoOrErr.takeError(); 126 127 // A null module means an error has already been reported. Return an empty 128 // result. 129 if (!Info) 130 return DIGlobal(); 131 132 // If the user is giving us relative addresses, add the preferred base of 133 // the object to the offset before we do the query. It's what DIContext 134 // expects. 135 if (Opts.RelativeAddresses) 136 ModuleOffset.Address += Info->getModulePreferredBase(); 137 138 DIGlobal Global = Info->symbolizeData(ModuleOffset); 139 if (Opts.Demangle) 140 Global.Name = DemangleName(Global.Name, Info); 141 return Global; 142 } 143 144 Expected<std::vector<DILocal>> 145 LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName, 146 object::SectionedAddress ModuleOffset) { 147 SymbolizableModule *Info; 148 if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) 149 Info = InfoOrErr.get(); 150 else 151 return InfoOrErr.takeError(); 152 153 // A null module means an error has already been reported. Return an empty 154 // result. 155 if (!Info) 156 return std::vector<DILocal>(); 157 158 // If the user is giving us relative addresses, add the preferred base of 159 // the object to the offset before we do the query. It's what DIContext 160 // expects. 161 if (Opts.RelativeAddresses) 162 ModuleOffset.Address += Info->getModulePreferredBase(); 163 164 return Info->symbolizeFrame(ModuleOffset); 165 } 166 167 void LLVMSymbolizer::flush() { 168 ObjectForUBPathAndArch.clear(); 169 BinaryForPath.clear(); 170 ObjectPairForPathArch.clear(); 171 Modules.clear(); 172 } 173 174 namespace { 175 176 // For Path="/path/to/foo" and Basename="foo" assume that debug info is in 177 // /path/to/foo.dSYM/Contents/Resources/DWARF/foo. 178 // For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in 179 // /path/to/bar.dSYM/Contents/Resources/DWARF/foo. 180 std::string getDarwinDWARFResourceForPath( 181 const std::string &Path, const std::string &Basename) { 182 SmallString<16> ResourceName = StringRef(Path); 183 if (sys::path::extension(Path) != ".dSYM") { 184 ResourceName += ".dSYM"; 185 } 186 sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); 187 sys::path::append(ResourceName, Basename); 188 return std::string(ResourceName.str()); 189 } 190 191 bool checkFileCRC(StringRef Path, uint32_t CRCHash) { 192 ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 193 MemoryBuffer::getFileOrSTDIN(Path); 194 if (!MB) 195 return false; 196 return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer())); 197 } 198 199 bool findDebugBinary(const std::string &OrigPath, 200 const std::string &DebuglinkName, uint32_t CRCHash, 201 const std::string &FallbackDebugPath, 202 std::string &Result) { 203 SmallString<16> OrigDir(OrigPath); 204 llvm::sys::path::remove_filename(OrigDir); 205 SmallString<16> DebugPath = OrigDir; 206 // Try relative/path/to/original_binary/debuglink_name 207 llvm::sys::path::append(DebugPath, DebuglinkName); 208 if (checkFileCRC(DebugPath, CRCHash)) { 209 Result = std::string(DebugPath.str()); 210 return true; 211 } 212 // Try relative/path/to/original_binary/.debug/debuglink_name 213 DebugPath = OrigDir; 214 llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); 215 if (checkFileCRC(DebugPath, CRCHash)) { 216 Result = std::string(DebugPath.str()); 217 return true; 218 } 219 // Make the path absolute so that lookups will go to 220 // "/usr/lib/debug/full/path/to/debug", not 221 // "/usr/lib/debug/to/debug" 222 llvm::sys::fs::make_absolute(OrigDir); 223 if (!FallbackDebugPath.empty()) { 224 // Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name 225 DebugPath = FallbackDebugPath; 226 } else { 227 #if defined(__NetBSD__) 228 // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name 229 DebugPath = "/usr/libdata/debug"; 230 #else 231 // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name 232 DebugPath = "/usr/lib/debug"; 233 #endif 234 } 235 llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), 236 DebuglinkName); 237 if (checkFileCRC(DebugPath, CRCHash)) { 238 Result = std::string(DebugPath.str()); 239 return true; 240 } 241 return false; 242 } 243 244 bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, 245 uint32_t &CRCHash) { 246 if (!Obj) 247 return false; 248 for (const SectionRef &Section : Obj->sections()) { 249 StringRef Name; 250 if (Expected<StringRef> NameOrErr = Section.getName()) 251 Name = *NameOrErr; 252 else 253 consumeError(NameOrErr.takeError()); 254 255 Name = Name.substr(Name.find_first_not_of("._")); 256 if (Name == "gnu_debuglink") { 257 Expected<StringRef> ContentsOrErr = Section.getContents(); 258 if (!ContentsOrErr) { 259 consumeError(ContentsOrErr.takeError()); 260 return false; 261 } 262 DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0); 263 uint64_t Offset = 0; 264 if (const char *DebugNameStr = DE.getCStr(&Offset)) { 265 // 4-byte align the offset. 266 Offset = (Offset + 3) & ~0x3; 267 if (DE.isValidOffsetForDataOfSize(Offset, 4)) { 268 DebugName = DebugNameStr; 269 CRCHash = DE.getU32(&Offset); 270 return true; 271 } 272 } 273 break; 274 } 275 } 276 return false; 277 } 278 279 bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, 280 const MachOObjectFile *Obj) { 281 ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid(); 282 ArrayRef<uint8_t> bin_uuid = Obj->getUuid(); 283 if (dbg_uuid.empty() || bin_uuid.empty()) 284 return false; 285 return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); 286 } 287 288 template <typename ELFT> 289 Optional<ArrayRef<uint8_t>> getBuildID(const ELFFile<ELFT> *Obj) { 290 if (!Obj) 291 return {}; 292 auto PhdrsOrErr = Obj->program_headers(); 293 if (!PhdrsOrErr) { 294 consumeError(PhdrsOrErr.takeError()); 295 return {}; 296 } 297 for (const auto &P : *PhdrsOrErr) { 298 if (P.p_type != ELF::PT_NOTE) 299 continue; 300 Error Err = Error::success(); 301 for (auto N : Obj->notes(P, Err)) 302 if (N.getType() == ELF::NT_GNU_BUILD_ID && N.getName() == ELF::ELF_NOTE_GNU) 303 return N.getDesc(); 304 consumeError(std::move(Err)); 305 } 306 return {}; 307 } 308 309 Optional<ArrayRef<uint8_t>> getBuildID(const ELFObjectFileBase *Obj) { 310 Optional<ArrayRef<uint8_t>> BuildID; 311 if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Obj)) 312 BuildID = getBuildID(O->getELFFile()); 313 else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Obj)) 314 BuildID = getBuildID(O->getELFFile()); 315 else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Obj)) 316 BuildID = getBuildID(O->getELFFile()); 317 else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Obj)) 318 BuildID = getBuildID(O->getELFFile()); 319 else 320 llvm_unreachable("unsupported file format"); 321 return BuildID; 322 } 323 324 bool findDebugBinary(const std::vector<std::string> &DebugFileDirectory, 325 const ArrayRef<uint8_t> BuildID, 326 std::string &Result) { 327 auto getDebugPath = [&](StringRef Directory) { 328 SmallString<128> Path{Directory}; 329 sys::path::append(Path, ".build-id", 330 llvm::toHex(BuildID[0], /*LowerCase=*/true), 331 llvm::toHex(BuildID.slice(1), /*LowerCase=*/true)); 332 Path += ".debug"; 333 return Path; 334 }; 335 if (DebugFileDirectory.empty()) { 336 SmallString<128> Path = getDebugPath( 337 #if defined(__NetBSD__) 338 // Try /usr/libdata/debug/.build-id/../... 339 "/usr/libdata/debug" 340 #else 341 // Try /usr/lib/debug/.build-id/../... 342 "/usr/lib/debug" 343 #endif 344 ); 345 if (llvm::sys::fs::exists(Path)) { 346 Result = std::string(Path.str()); 347 return true; 348 } 349 } else { 350 for (const auto &Directory : DebugFileDirectory) { 351 // Try <debug-file-directory>/.build-id/../... 352 SmallString<128> Path = getDebugPath(Directory); 353 if (llvm::sys::fs::exists(Path)) { 354 Result = std::string(Path.str()); 355 return true; 356 } 357 } 358 } 359 return false; 360 } 361 362 } // end anonymous namespace 363 364 ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, 365 const MachOObjectFile *MachExeObj, const std::string &ArchName) { 366 // On Darwin we may find DWARF in separate object file in 367 // resource directory. 368 std::vector<std::string> DsymPaths; 369 StringRef Filename = sys::path::filename(ExePath); 370 DsymPaths.push_back( 371 getDarwinDWARFResourceForPath(ExePath, std::string(Filename))); 372 for (const auto &Path : Opts.DsymHints) { 373 DsymPaths.push_back( 374 getDarwinDWARFResourceForPath(Path, std::string(Filename))); 375 } 376 for (const auto &Path : DsymPaths) { 377 auto DbgObjOrErr = getOrCreateObject(Path, ArchName); 378 if (!DbgObjOrErr) { 379 // Ignore errors, the file might not exist. 380 consumeError(DbgObjOrErr.takeError()); 381 continue; 382 } 383 ObjectFile *DbgObj = DbgObjOrErr.get(); 384 if (!DbgObj) 385 continue; 386 const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj); 387 if (!MachDbgObj) 388 continue; 389 if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) 390 return DbgObj; 391 } 392 return nullptr; 393 } 394 395 ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path, 396 const ObjectFile *Obj, 397 const std::string &ArchName) { 398 std::string DebuglinkName; 399 uint32_t CRCHash; 400 std::string DebugBinaryPath; 401 if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash)) 402 return nullptr; 403 if (!findDebugBinary(Path, DebuglinkName, CRCHash, Opts.FallbackDebugPath, 404 DebugBinaryPath)) 405 return nullptr; 406 auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); 407 if (!DbgObjOrErr) { 408 // Ignore errors, the file might not exist. 409 consumeError(DbgObjOrErr.takeError()); 410 return nullptr; 411 } 412 return DbgObjOrErr.get(); 413 } 414 415 ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path, 416 const ELFObjectFileBase *Obj, 417 const std::string &ArchName) { 418 auto BuildID = getBuildID(Obj); 419 if (!BuildID) 420 return nullptr; 421 if (BuildID->size() < 2) 422 return nullptr; 423 std::string DebugBinaryPath; 424 if (!findDebugBinary(Opts.DebugFileDirectory, *BuildID, DebugBinaryPath)) 425 return nullptr; 426 auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); 427 if (!DbgObjOrErr) { 428 consumeError(DbgObjOrErr.takeError()); 429 return nullptr; 430 } 431 return DbgObjOrErr.get(); 432 } 433 434 Expected<LLVMSymbolizer::ObjectPair> 435 LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, 436 const std::string &ArchName) { 437 auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); 438 if (I != ObjectPairForPathArch.end()) 439 return I->second; 440 441 auto ObjOrErr = getOrCreateObject(Path, ArchName); 442 if (!ObjOrErr) { 443 ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), 444 ObjectPair(nullptr, nullptr)); 445 return ObjOrErr.takeError(); 446 } 447 448 ObjectFile *Obj = ObjOrErr.get(); 449 assert(Obj != nullptr); 450 ObjectFile *DbgObj = nullptr; 451 452 if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj)) 453 DbgObj = lookUpDsymFile(Path, MachObj, ArchName); 454 else if (auto ELFObj = dyn_cast<const ELFObjectFileBase>(Obj)) 455 DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName); 456 if (!DbgObj) 457 DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName); 458 if (!DbgObj) 459 DbgObj = Obj; 460 ObjectPair Res = std::make_pair(Obj, DbgObj); 461 ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res); 462 return Res; 463 } 464 465 Expected<ObjectFile *> 466 LLVMSymbolizer::getOrCreateObject(const std::string &Path, 467 const std::string &ArchName) { 468 Binary *Bin; 469 auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>()); 470 if (!Pair.second) { 471 Bin = Pair.first->second.getBinary(); 472 } else { 473 Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path); 474 if (!BinOrErr) 475 return BinOrErr.takeError(); 476 Pair.first->second = std::move(BinOrErr.get()); 477 Bin = Pair.first->second.getBinary(); 478 } 479 480 if (!Bin) 481 return static_cast<ObjectFile *>(nullptr); 482 483 if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) { 484 auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); 485 if (I != ObjectForUBPathAndArch.end()) 486 return I->second.get(); 487 488 Expected<std::unique_ptr<ObjectFile>> ObjOrErr = 489 UB->getMachOObjectForArch(ArchName); 490 if (!ObjOrErr) { 491 ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), 492 std::unique_ptr<ObjectFile>()); 493 return ObjOrErr.takeError(); 494 } 495 ObjectFile *Res = ObjOrErr->get(); 496 ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), 497 std::move(ObjOrErr.get())); 498 return Res; 499 } 500 if (Bin->isObject()) { 501 return cast<ObjectFile>(Bin); 502 } 503 return errorCodeToError(object_error::arch_not_found); 504 } 505 506 Expected<SymbolizableModule *> 507 LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj, 508 std::unique_ptr<DIContext> Context, 509 StringRef ModuleName) { 510 auto InfoOrErr = SymbolizableObjectFile::create(Obj, std::move(Context), 511 Opts.UntagAddresses); 512 std::unique_ptr<SymbolizableModule> SymMod; 513 if (InfoOrErr) 514 SymMod = std::move(*InfoOrErr); 515 auto InsertResult = Modules.insert( 516 std::make_pair(std::string(ModuleName), std::move(SymMod))); 517 assert(InsertResult.second); 518 if (!InfoOrErr) 519 return InfoOrErr.takeError(); 520 return InsertResult.first->second.get(); 521 } 522 523 Expected<SymbolizableModule *> 524 LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { 525 auto I = Modules.find(ModuleName); 526 if (I != Modules.end()) 527 return I->second.get(); 528 529 std::string BinaryName = ModuleName; 530 std::string ArchName = Opts.DefaultArch; 531 size_t ColonPos = ModuleName.find_last_of(':'); 532 // Verify that substring after colon form a valid arch name. 533 if (ColonPos != std::string::npos) { 534 std::string ArchStr = ModuleName.substr(ColonPos + 1); 535 if (Triple(ArchStr).getArch() != Triple::UnknownArch) { 536 BinaryName = ModuleName.substr(0, ColonPos); 537 ArchName = ArchStr; 538 } 539 } 540 auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName); 541 if (!ObjectsOrErr) { 542 // Failed to find valid object file. 543 Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); 544 return ObjectsOrErr.takeError(); 545 } 546 ObjectPair Objects = ObjectsOrErr.get(); 547 548 std::unique_ptr<DIContext> Context; 549 // If this is a COFF object containing PDB info, use a PDBContext to 550 // symbolize. Otherwise, use DWARF. 551 if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) { 552 const codeview::DebugInfo *DebugInfo; 553 StringRef PDBFileName; 554 auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName); 555 if (!EC && DebugInfo != nullptr && !PDBFileName.empty()) { 556 #if 0 557 using namespace pdb; 558 std::unique_ptr<IPDBSession> Session; 559 PDB_ReaderType ReaderType = Opts.UseNativePDBReader 560 ? PDB_ReaderType::Native 561 : PDB_ReaderType::DIA; 562 if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(), 563 Session)) { 564 Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); 565 // Return along the PDB filename to provide more context 566 return createFileError(PDBFileName, std::move(Err)); 567 } 568 Context.reset(new PDBContext(*CoffObject, std::move(Session))); 569 #else 570 return make_error<StringError>( 571 "PDB support not compiled in", 572 std::make_error_code(std::errc::not_supported)); 573 #endif 574 } 575 } 576 if (!Context) 577 Context = DWARFContext::create(*Objects.second, nullptr, Opts.DWPName); 578 return createModuleInfo(Objects.first, std::move(Context), ModuleName); 579 } 580 581 namespace { 582 583 // Undo these various manglings for Win32 extern "C" functions: 584 // cdecl - _foo 585 // stdcall - _foo@12 586 // fastcall - @foo@12 587 // vectorcall - foo@@12 588 // These are all different linkage names for 'foo'. 589 StringRef demanglePE32ExternCFunc(StringRef SymbolName) { 590 // Remove any '_' or '@' prefix. 591 char Front = SymbolName.empty() ? '\0' : SymbolName[0]; 592 if (Front == '_' || Front == '@') 593 SymbolName = SymbolName.drop_front(); 594 595 // Remove any '@[0-9]+' suffix. 596 if (Front != '?') { 597 size_t AtPos = SymbolName.rfind('@'); 598 if (AtPos != StringRef::npos && 599 std::all_of(SymbolName.begin() + AtPos + 1, SymbolName.end(), 600 [](char C) { return C >= '0' && C <= '9'; })) { 601 SymbolName = SymbolName.substr(0, AtPos); 602 } 603 } 604 605 // Remove any ending '@' for vectorcall. 606 if (SymbolName.endswith("@")) 607 SymbolName = SymbolName.drop_back(); 608 609 return SymbolName; 610 } 611 612 } // end anonymous namespace 613 614 std::string 615 LLVMSymbolizer::DemangleName(const std::string &Name, 616 const SymbolizableModule *DbiModuleDescriptor) { 617 // We can spoil names of symbols with C linkage, so use an heuristic 618 // approach to check if the name should be demangled. 619 if (Name.substr(0, 2) == "_Z") { 620 int status = 0; 621 char *DemangledName = itaniumDemangle(Name.c_str(), nullptr, nullptr, &status); 622 if (status != 0) 623 return Name; 624 std::string Result = DemangledName; 625 free(DemangledName); 626 return Result; 627 } 628 629 if (!Name.empty() && Name.front() == '?') { 630 // Only do MSVC C++ demangling on symbols starting with '?'. 631 int status = 0; 632 char *DemangledName = microsoftDemangle( 633 Name.c_str(), nullptr, nullptr, nullptr, &status, 634 MSDemangleFlags(MSDF_NoAccessSpecifier | MSDF_NoCallingConvention | 635 MSDF_NoMemberType | MSDF_NoReturnType)); 636 if (status != 0) 637 return Name; 638 std::string Result = DemangledName; 639 free(DemangledName); 640 return Result; 641 } 642 643 if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) 644 return std::string(demanglePE32ExternCFunc(Name)); 645 return Name; 646 } 647 648 } // namespace symbolize 649 } // namespace llvm 650