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