574 lines · cpp
1//===-- Mangled.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Core/Mangled.h"10 11#include "lldb/Core/DataFileCache.h"12#include "lldb/Core/DemangledNameInfo.h"13#include "lldb/Core/RichManglingContext.h"14#include "lldb/Target/Language.h"15#include "lldb/Utility/ConstString.h"16#include "lldb/Utility/DataEncoder.h"17#include "lldb/Utility/LLDBLog.h"18#include "lldb/Utility/Log.h"19#include "lldb/Utility/RegularExpression.h"20#include "lldb/Utility/Stream.h"21#include "lldb/lldb-enumerations.h"22 23#include "llvm/ADT/StringExtras.h"24#include "llvm/ADT/StringRef.h"25#include "llvm/Demangle/Demangle.h"26#include "llvm/Support/Compiler.h"27 28#include <mutex>29#include <string>30#include <string_view>31#include <utility>32 33#include <cstdlib>34#include <cstring>35using namespace lldb_private;36 37#pragma mark Mangled38 39bool Mangled::IsMangledName(llvm::StringRef name) {40 return Mangled::GetManglingScheme(name) != Mangled::eManglingSchemeNone;41}42 43Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef name) {44 if (name.empty())45 return Mangled::eManglingSchemeNone;46 47 if (name.starts_with("?"))48 return Mangled::eManglingSchemeMSVC;49 50 if (name.starts_with("_R"))51 return Mangled::eManglingSchemeRustV0;52 53 if (name.starts_with("_D")) {54 // A dlang mangled name begins with `_D`, followed by a numeric length. One55 // known exception is the symbol `_Dmain`.56 // See `SymbolName` and `LName` in57 // https://dlang.org/spec/abi.html#name_mangling58 llvm::StringRef buf = name.drop_front(2);59 if (!buf.empty() && (llvm::isDigit(buf.front()) || name == "_Dmain"))60 return Mangled::eManglingSchemeD;61 }62 63 if (name.starts_with("_Z"))64 return Mangled::eManglingSchemeItanium;65 66 // ___Z is a clang extension of block invocations67 if (name.starts_with("___Z"))68 return Mangled::eManglingSchemeItanium;69 70 // Swift's older style of mangling used "_T" as a mangling prefix. This can71 // lead to false positives with other symbols that just so happen to start72 // with "_T". To minimize the chance of that happening, we only return true73 // for select old-style swift mangled names. The known cases are ObjC classes74 // and protocols. Classes are either prefixed with "_TtC" or "_TtGC".75 // Protocols are prefixed with "_TtP".76 if (name.starts_with("_TtC") || name.starts_with("_TtGC") ||77 name.starts_with("_TtP"))78 return Mangled::eManglingSchemeSwift;79 80 // Swift 4.2 used "$S" and "_$S".81 // Swift 5 and onward uses "$s" and "_$s".82 // Swift also uses "@__swiftmacro_" as a prefix for mangling filenames.83 // Embedded Swift introduced "$e" and "_$e" as Swift mangling prefixes.84 if (name.starts_with("$S") || name.starts_with("_$S") ||85 name.starts_with("$s") || name.starts_with("_$s") ||86 name.starts_with("$e") || name.starts_with("_$e") ||87 name.starts_with("@__swiftmacro_"))88 return Mangled::eManglingSchemeSwift;89 90 return Mangled::eManglingSchemeNone;91}92 93Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() {94 if (s)95 SetValue(s);96}97 98Mangled::Mangled(llvm::StringRef name) {99 if (!name.empty())100 SetValue(ConstString(name));101}102 103// Convert to bool operator. This allows code to check any Mangled objects104// to see if they contain anything valid using code such as:105//106// Mangled mangled(...);107// if (mangled)108// { ...109Mangled::operator bool() const { return m_mangled || m_demangled; }110 111// Clear the mangled and demangled values.112void Mangled::Clear() {113 m_mangled.Clear();114 m_demangled.Clear();115 m_demangled_info.reset();116}117 118// Compare the string values.119int Mangled::Compare(const Mangled &a, const Mangled &b) {120 return ConstString::Compare(a.GetName(ePreferMangled),121 b.GetName(ePreferMangled));122}123 124void Mangled::SetValue(ConstString name) {125 if (name) {126 if (IsMangledName(name.GetStringRef())) {127 m_demangled.Clear();128 m_mangled = name;129 m_demangled_info.reset();130 } else {131 m_demangled = name;132 m_mangled.Clear();133 m_demangled_info.reset();134 }135 } else {136 m_demangled.Clear();137 m_mangled.Clear();138 m_demangled_info.reset();139 }140}141 142// Local helpers for different demangling implementations.143static char *GetMSVCDemangledStr(llvm::StringRef M) {144 char *demangled_cstr = llvm::microsoftDemangle(145 M, nullptr, nullptr,146 llvm::MSDemangleFlags(147 llvm::MSDF_NoAccessSpecifier | llvm::MSDF_NoCallingConvention |148 llvm::MSDF_NoMemberType | llvm::MSDF_NoVariableType));149 150 if (Log *log = GetLog(LLDBLog::Demangle)) {151 if (demangled_cstr && demangled_cstr[0])152 LLDB_LOGF(log, "demangled msvc: %s -> \"%s\"", M.data(), demangled_cstr);153 else154 LLDB_LOGF(log, "demangled msvc: %s -> error", M.data());155 }156 157 return demangled_cstr;158}159 160static std::pair<char *, DemangledNameInfo>161GetItaniumDemangledStr(const char *M) {162 char *demangled_cstr = nullptr;163 164 DemangledNameInfo info;165 llvm::ItaniumPartialDemangler ipd;166 bool err = ipd.partialDemangle(M);167 if (!err) {168 // Default buffer and size (OutputBuffer will realloc in case it's too169 // small).170 size_t demangled_size = 80;171 demangled_cstr = static_cast<char *>(std::malloc(80));172 173 TrackingOutputBuffer OB(demangled_cstr, demangled_size);174 demangled_cstr = ipd.finishDemangle(&OB);175 info = std::move(OB.NameInfo);176 177 assert(demangled_cstr &&178 "finishDemangle must always succeed if partialDemangle did");179 assert(demangled_cstr[OB.getCurrentPosition() - 1] == '\0' &&180 "Expected demangled_size to return length including trailing null");181 }182 183 if (Log *log = GetLog(LLDBLog::Demangle)) {184 if (demangled_cstr)185 LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr);186 else187 LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M);188 189 if (!info.hasBasename())190 LLDB_LOGF(log,191 "demangled itanium: %s -> error: failed to retrieve name info",192 M);193 }194 195 return {demangled_cstr, std::move(info)};196}197 198static char *GetRustV0DemangledStr(llvm::StringRef M) {199 char *demangled_cstr = llvm::rustDemangle(M);200 201 if (Log *log = GetLog(LLDBLog::Demangle)) {202 if (demangled_cstr && demangled_cstr[0])203 LLDB_LOG(log, "demangled rustv0: {0} -> \"{1}\"", M, demangled_cstr);204 else205 LLDB_LOG(log, "demangled rustv0: {0} -> error: failed to demangle",206 static_cast<std::string_view>(M));207 }208 209 return demangled_cstr;210}211 212static char *GetDLangDemangledStr(llvm::StringRef M) {213 char *demangled_cstr = llvm::dlangDemangle(M);214 215 if (Log *log = GetLog(LLDBLog::Demangle)) {216 if (demangled_cstr && demangled_cstr[0])217 LLDB_LOG(log, "demangled dlang: {0} -> \"{1}\"", M, demangled_cstr);218 else219 LLDB_LOG(log, "demangled dlang: {0} -> error: failed to demangle",220 static_cast<std::string_view>(M));221 }222 223 return demangled_cstr;224}225 226// Explicit demangling for scheduled requests during batch processing. This227// makes use of ItaniumPartialDemangler's rich demangle info228bool Mangled::GetRichManglingInfo(RichManglingContext &context,229 SkipMangledNameFn *skip_mangled_name) {230 // Others are not meant to arrive here. ObjC names or C's main() for example231 // have their names stored in m_demangled, while m_mangled is empty.232 assert(m_mangled);233 234 // Check whether or not we are interested in this name at all.235 ManglingScheme scheme = GetManglingScheme(m_mangled.GetStringRef());236 if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme))237 return false;238 239 switch (scheme) {240 case eManglingSchemeNone:241 // The current mangled_name_filter would allow llvm_unreachable here.242 return false;243 244 case eManglingSchemeItanium:245 // We want the rich mangling info here, so we don't care whether or not246 // there is a demangled string in the pool already.247 return context.FromItaniumName(m_mangled);248 249 case eManglingSchemeMSVC: {250 // We have no rich mangling for MSVC-mangled names yet, so first try to251 // demangle it if necessary.252 if (!m_demangled && !m_mangled.GetMangledCounterpart(m_demangled)) {253 if (char *d = GetMSVCDemangledStr(m_mangled)) {254 // Without the rich mangling info we have to demangle the full name.255 // Copy it to string pool and connect the counterparts to accelerate256 // later access in GetDemangledName().257 m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(d),258 m_mangled);259 ::free(d);260 } else {261 m_demangled.SetCString("");262 }263 }264 265 if (m_demangled.IsEmpty()) {266 // Cannot demangle it, so don't try parsing.267 return false;268 } else {269 // Demangled successfully, we can try and parse it with270 // CPlusPlusLanguage::CxxMethodName.271 return context.FromCxxMethodName(m_demangled);272 }273 }274 275 case eManglingSchemeRustV0:276 case eManglingSchemeD:277 case eManglingSchemeSwift:278 // Rich demangling scheme is not supported279 return false;280 }281 llvm_unreachable("Fully covered switch above!");282}283 284ConstString Mangled::GetDemangledName() const {285 return GetDemangledNameImpl(/*force=*/false);286}287 288std::optional<DemangledNameInfo> const &Mangled::GetDemangledInfo() const {289 if (!m_demangled_info)290 GetDemangledNameImpl(/*force=*/true);291 292 return m_demangled_info;293}294 295// Generate the demangled name on demand using this accessor. Code in this296// class will need to use this accessor if it wishes to decode the demangled297// name. The result is cached and will be kept until a new string value is298// supplied to this object, or until the end of the object's lifetime.299ConstString Mangled::GetDemangledNameImpl(bool force) const {300 if (!m_mangled)301 return m_demangled;302 303 // Re-use previously demangled names.304 if (!force && !m_demangled.IsNull())305 return m_demangled;306 307 if (!force && m_mangled.GetMangledCounterpart(m_demangled) &&308 !m_demangled.IsNull())309 return m_demangled;310 311 // We didn't already mangle this name, demangle it and if all goes well312 // add it to our map.313 char *demangled_name = nullptr;314 switch (GetManglingScheme(m_mangled.GetStringRef())) {315 case eManglingSchemeMSVC:316 demangled_name = GetMSVCDemangledStr(m_mangled);317 break;318 case eManglingSchemeItanium: {319 std::pair<char *, DemangledNameInfo> demangled =320 GetItaniumDemangledStr(m_mangled.GetCString());321 demangled_name = demangled.first;322 m_demangled_info.emplace(std::move(demangled.second));323 break;324 }325 case eManglingSchemeRustV0:326 demangled_name = GetRustV0DemangledStr(m_mangled);327 break;328 case eManglingSchemeD:329 demangled_name = GetDLangDemangledStr(m_mangled);330 break;331 case eManglingSchemeSwift:332 // Demangling a swift name requires the swift compiler. This is333 // explicitly unsupported on llvm.org.334 break;335 case eManglingSchemeNone:336 // Don't bother demangling anything that isn't mangled.337 break;338 }339 340 if (demangled_name) {341 m_demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled);342 free(demangled_name);343 }344 345 if (m_demangled.IsNull()) {346 // Set the demangled string to the empty string to indicate we tried to347 // parse it once and failed.348 m_demangled.SetCString("");349 }350 351 return m_demangled;352}353 354ConstString Mangled::GetDisplayDemangledName() const {355 if (Language *lang = Language::FindPlugin(GuessLanguage()))356 return lang->GetDisplayDemangledName(*this);357 return GetDemangledName();358}359 360bool Mangled::NameMatches(const RegularExpression ®ex) const {361 if (m_mangled && regex.Execute(m_mangled.GetStringRef()))362 return true;363 364 ConstString demangled = GetDemangledName();365 return demangled && regex.Execute(demangled.GetStringRef());366}367 368// Get the demangled name if there is one, else return the mangled name.369ConstString Mangled::GetName(Mangled::NamePreference preference) const {370 if (preference == ePreferMangled && m_mangled)371 return m_mangled;372 373 // Call the accessor to make sure we get a demangled name in case it hasn't374 // been demangled yet...375 ConstString demangled = GetDemangledName();376 377 if (preference == ePreferDemangledWithoutArguments) {378 if (Language *lang = Language::FindPlugin(GuessLanguage())) {379 return lang->GetDemangledFunctionNameWithoutArguments(*this);380 }381 }382 if (preference == ePreferDemangled) {383 if (demangled)384 return demangled;385 return m_mangled;386 }387 return demangled;388}389 390// Dump a Mangled object to stream "s". We don't force our demangled name to be391// computed currently (we don't use the accessor).392void Mangled::Dump(Stream *s) const {393 if (m_mangled) {394 *s << ", mangled = " << m_mangled;395 }396 if (m_demangled) {397 const char *demangled = m_demangled.AsCString();398 s->Printf(", demangled = %s", demangled[0] ? demangled : "<error>");399 }400}401 402// Dumps a debug version of this string with extra object and state information403// to stream "s".404void Mangled::DumpDebug(Stream *s) const {405 s->Printf("%*p: Mangled mangled = ", static_cast<int>(sizeof(void *) * 2),406 static_cast<const void *>(this));407 m_mangled.DumpDebug(s);408 s->Printf(", demangled = ");409 m_demangled.DumpDebug(s);410}411 412// Return the size in byte that this object takes in memory. The size includes413// the size of the objects it owns, and not the strings that it references414// because they are shared strings.415size_t Mangled::MemorySize() const {416 return m_mangled.MemorySize() + m_demangled.MemorySize();417}418 419// We "guess" the language because we can't determine a symbol's language from420// it's name. For example, a Pascal symbol can be mangled using the C++421// Itanium scheme, and defined in a compilation unit within the same module as422// other C++ units. In addition, different targets could have different ways423// of mangling names from a given language, likewise the compilation units424// within those targets.425lldb::LanguageType Mangled::GuessLanguage() const {426 lldb::LanguageType result = lldb::eLanguageTypeUnknown;427 // Ask each language plugin to check if the mangled name belongs to it.428 Language::ForEach([this, &result](Language *l) {429 if (l->SymbolNameFitsToLanguage(*this)) {430 result = l->GetLanguageType();431 return IterationAction::Stop;432 }433 return IterationAction::Continue;434 });435 return result;436}437 438// Dump OBJ to the supplied stream S.439Stream &operator<<(Stream &s, const Mangled &obj) {440 if (obj.GetMangledName())441 s << "mangled = '" << obj.GetMangledName() << "'";442 443 ConstString demangled = obj.GetDemangledName();444 if (demangled)445 s << ", demangled = '" << demangled << '\'';446 else447 s << ", demangled = <error>";448 return s;449}450 451// When encoding Mangled objects we can get away with encoding as little452// information as is required. The enumeration below helps us to efficiently453// encode Mangled objects.454enum MangledEncoding {455 /// If the Mangled object has neither a mangled name or demangled name we can456 /// encode the object with one zero byte using the Empty enumeration.457 Empty = 0u,458 /// If the Mangled object has only a demangled name and no mangled named, we459 /// can encode only the demangled name.460 DemangledOnly = 1u,461 /// If the mangle name can calculate the demangled name (it is the462 /// mangled/demangled counterpart), then we only need to encode the mangled463 /// name as the demangled name can be recomputed.464 MangledOnly = 2u,465 /// If we have a Mangled object with two different names that are not related466 /// then we need to save both strings. This can happen if we have a name that467 /// isn't a true mangled name, but we want to be able to lookup a symbol by468 /// name and type in the symbol table. We do this for Objective C symbols like469 /// "OBJC_CLASS_$_NSValue" where the mangled named will be set to470 /// "OBJC_CLASS_$_NSValue" and the demangled name will be manually set to471 /// "NSValue". If we tried to demangled the name "OBJC_CLASS_$_NSValue" it472 /// would fail, but in these cases we want these unrelated names to be473 /// preserved.474 MangledAndDemangled = 3u475};476 477bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,478 const StringTableReader &strtab) {479 m_mangled.Clear();480 m_demangled.Clear();481 m_demangled_info.reset();482 MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr);483 switch (encoding) {484 case Empty:485 return true;486 487 case DemangledOnly:488 m_demangled.SetString(strtab.Get(data.GetU32(offset_ptr)));489 return true;490 491 case MangledOnly:492 m_mangled.SetString(strtab.Get(data.GetU32(offset_ptr)));493 return true;494 495 case MangledAndDemangled:496 m_mangled.SetString(strtab.Get(data.GetU32(offset_ptr)));497 m_demangled.SetString(strtab.Get(data.GetU32(offset_ptr)));498 return true;499 }500 return false;501}502/// The encoding format for the Mangled object is as follows:503///504/// uint8_t encoding;505/// char str1[]; (only if DemangledOnly, MangledOnly)506/// char str2[]; (only if MangledAndDemangled)507///508/// The strings are stored as NULL terminated UTF8 strings and str1 and str2509/// are only saved if we need them based on the encoding.510///511/// Some mangled names have a mangled name that can be demangled by the built512/// in demanglers. These kinds of mangled objects know when the mangled and513/// demangled names are the counterparts for each other. This is done because514/// demangling is very expensive and avoiding demangling the same name twice515/// saves us a lot of compute time. For these kinds of names we only need to516/// save the mangled name and have the encoding set to "MangledOnly".517///518/// If a mangled obejct has only a demangled name, then we save only that string519/// and have the encoding set to "DemangledOnly".520///521/// Some mangled objects have both mangled and demangled names, but the522/// demangled name can not be computed from the mangled name. This is often used523/// for runtime named, like Objective C runtime V2 and V3 names. Both these524/// names must be saved and the encoding is set to "MangledAndDemangled".525///526/// For a Mangled object with no names, we only need to set the encoding to527/// "Empty" and not store any string values.528void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {529 MangledEncoding encoding = Empty;530 if (m_mangled) {531 encoding = MangledOnly;532 if (m_demangled) {533 // We have both mangled and demangled names. If the demangled name is the534 // counterpart of the mangled name, then we only need to save the mangled535 // named. If they are different, we need to save both.536 ConstString s;537 if (!(m_mangled.GetMangledCounterpart(s) && s == m_demangled))538 encoding = MangledAndDemangled;539 }540 } else if (m_demangled) {541 encoding = DemangledOnly;542 }543 file.AppendU8(encoding);544 switch (encoding) {545 case Empty:546 break;547 case DemangledOnly:548 file.AppendU32(strtab.Add(m_demangled));549 break;550 case MangledOnly:551 file.AppendU32(strtab.Add(m_mangled));552 break;553 case MangledAndDemangled:554 file.AppendU32(strtab.Add(m_mangled));555 file.AppendU32(strtab.Add(m_demangled));556 break;557 }558}559 560ConstString Mangled::GetBaseName() const {561 const auto &demangled_info = GetDemangledInfo();562 if (!demangled_info.has_value())563 return {};564 565 ConstString demangled_name = GetDemangledName();566 if (!demangled_name)567 return {};568 569 const char *name_str = demangled_name.AsCString();570 const auto &range = demangled_info->BasenameRange;571 return ConstString(572 llvm::StringRef(name_str + range.first, range.second - range.first));573}574