llvm-project/llvm/lib/Demangle/MicrosoftDemangle.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2380 lines
76 KiB
C++
Raw Normal View History

//===- MicrosoftDemangle.cpp ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a demangler for MSVC-style mangled symbols.
//
// This file has no dependencies on the rest of LLVM so that it can be
// easily reused in other programs such as libcxxabi.
//
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/MicrosoftDemangle.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Demangle/MicrosoftDemangleNodes.h"
#include "llvm/Demangle/DemangleConfig.h"
Move Itanium demangler implementation into a header file and add visitation support. Summary: This transforms the Itanium demangler into a generic reusable library that can be used to build, traverse, and transform Itanium mangled name trees. This is in preparation for adding a canonicalizing demangler, which cannot live in the Demangle library for layering reasons. In order to keep the diffs simpler, this patch moves more code to the new header than is strictly necessary: in particular, all of the printLeft / printRight implementations can be moved to the implementation file. (And indeed we could make them non-virtual now if we wished, and remove the vptr from Node.) All nodes are now included in the Kind enumeration, rather than omitting some of the Expr nodes, and the three different floating-point literal node types now have distinct Kind values. As a proof of concept for the visitation / matching mechanism, this patch implements a Node dumping facility on top of it, replacing the prior mechanism that produced the pretty-printed output rather than a tree dump. Sample dump output: FunctionEncoding( NameType("int"), NameWithTemplateArgs( NestedName( NameWithTemplateArgs( NameType("A"), TemplateArgs( {NameType("B")})), NameType("f")), TemplateArgs( {NameType("int")})), {}, <null>, QualConst, FunctionRefQual::FrefQualLValue) As a next step, it would make sense to move the LLVM high-level interface to the demangler (the itaniumDemangler function and ItaniumPartialDemangler class) into the Support library, and implement them in terms of the Demangle library. This would allow the libc++abi demangler implementation to be an identical copy of the llvm Demangle library, and would allow the LLVM implementation to reuse LLVM components such as llvm::BumpPtrAllocator, but we'll need to decide how to coordinate that with the MS ABI demangler, so I'm not doing that in this patch. No functionality change intended other than the behavior of dump(). Reviewers: erik.pilkington, zturner, chandlerc, dlj Subscribers: aheejin, llvm-commits Differential Revision: https://reviews.llvm.org/D50930 llvm-svn: 340203
2018-08-21 03:44:01 +08:00
#include "llvm/Demangle/StringView.h"
#include "llvm/Demangle/Utility.h"
#include <array>
#include <cctype>
#include <cstdio>
#include <tuple>
using namespace llvm;
using namespace ms_demangle;
static bool startsWithDigit(StringView S) {
return !S.empty() && std::isdigit(S.front());
}
struct NodeList {
Node *N = nullptr;
NodeList *Next = nullptr;
};
static bool isMemberPointer(StringView MangledName, bool &Error) {
Error = false;
switch (MangledName.popFront()) {
case '$':
// This is probably an rvalue reference (e.g. $$Q), and you cannot have an
// rvalue reference to a member.
return false;
case 'A':
// 'A' indicates a reference, and you cannot have a reference to a member
// function or member.
return false;
case 'P':
case 'Q':
case 'R':
case 'S':
// These 4 values indicate some kind of pointer, but we still don't know
// what.
break;
default:
// isMemberPointer() is called only if isPointerType() returns true,
// and it rejects other prefixes.
DEMANGLE_UNREACHABLE;
}
// If it starts with a number, then 6 indicates a non-member function
// pointer, and 8 indicates a member function pointer.
if (startsWithDigit(MangledName)) {
if (MangledName[0] != '6' && MangledName[0] != '8') {
Error = true;
return false;
}
return (MangledName[0] == '8');
}
// Remove ext qualifiers since those can appear on either type and are
// therefore not indicative.
MangledName.consumeFront('E'); // 64-bit
MangledName.consumeFront('I'); // restrict
MangledName.consumeFront('F'); // unaligned
if (MangledName.empty()) {
Error = true;
return false;
}
// The next value should be either ABCD (non-member) or QRST (member).
switch (MangledName.front()) {
case 'A':
case 'B':
case 'C':
case 'D':
return false;
case 'Q':
case 'R':
case 'S':
case 'T':
return true;
default:
Error = true;
return false;
}
}
static SpecialIntrinsicKind
consumeSpecialIntrinsicKind(StringView &MangledName) {
if (MangledName.consumeFront("?_7"))
return SpecialIntrinsicKind::Vftable;
if (MangledName.consumeFront("?_8"))
return SpecialIntrinsicKind::Vbtable;
if (MangledName.consumeFront("?_9"))
return SpecialIntrinsicKind::VcallThunk;
if (MangledName.consumeFront("?_A"))
return SpecialIntrinsicKind::Typeof;
if (MangledName.consumeFront("?_B"))
return SpecialIntrinsicKind::LocalStaticGuard;
if (MangledName.consumeFront("?_C"))
return SpecialIntrinsicKind::StringLiteralSymbol;
if (MangledName.consumeFront("?_P"))
return SpecialIntrinsicKind::UdtReturning;
if (MangledName.consumeFront("?_R0"))
return SpecialIntrinsicKind::RttiTypeDescriptor;
if (MangledName.consumeFront("?_R1"))
return SpecialIntrinsicKind::RttiBaseClassDescriptor;
if (MangledName.consumeFront("?_R2"))
return SpecialIntrinsicKind::RttiBaseClassArray;
if (MangledName.consumeFront("?_R3"))
return SpecialIntrinsicKind::RttiClassHierarchyDescriptor;
if (MangledName.consumeFront("?_R4"))
return SpecialIntrinsicKind::RttiCompleteObjLocator;
if (MangledName.consumeFront("?_S"))
return SpecialIntrinsicKind::LocalVftable;
if (MangledName.consumeFront("?__E"))
return SpecialIntrinsicKind::DynamicInitializer;
if (MangledName.consumeFront("?__F"))
return SpecialIntrinsicKind::DynamicAtexitDestructor;
if (MangledName.consumeFront("?__J"))
return SpecialIntrinsicKind::LocalStaticThreadGuard;
return SpecialIntrinsicKind::None;
}
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
static bool startsWithLocalScopePattern(StringView S) {
if (!S.consumeFront('?'))
return false;
size_t End = S.find('?');
if (End == StringView::npos)
return false;
StringView Candidate = S.substr(0, End);
if (Candidate.empty())
return false;
// \?[0-9]\?
// ?@? is the discriminator 0.
if (Candidate.size() == 1)
return Candidate[0] == '@' || (Candidate[0] >= '0' && Candidate[0] <= '9');
// If it's not 0-9, then it's an encoded number terminated with an @
if (Candidate.back() != '@')
return false;
Candidate = Candidate.dropBack();
// An encoded number starts with B-P and all subsequent digits are in A-P.
// Note that the reason the first digit cannot be A is two fold. First, it
// would create an ambiguity with ?A which delimits the beginning of an
// anonymous namespace. Second, A represents 0, and you don't start a multi
// digit number with a leading 0. Presumably the anonymous namespace
// ambiguity is also why single digit encoded numbers use 0-9 rather than A-J.
if (Candidate[0] < 'B' || Candidate[0] > 'P')
return false;
Candidate = Candidate.dropFront();
while (!Candidate.empty()) {
if (Candidate[0] < 'A' || Candidate[0] > 'P')
return false;
Candidate = Candidate.dropFront();
}
return true;
}
static bool isTagType(StringView S) {
switch (S.front()) {
case 'T': // union
case 'U': // struct
case 'V': // class
case 'W': // enum
return true;
}
return false;
}
static bool isCustomType(StringView S) { return S[0] == '?'; }
static bool isPointerType(StringView S) {
if (S.startsWith("$$Q")) // foo &&
return true;
switch (S.front()) {
case 'A': // foo &
case 'P': // foo *
case 'Q': // foo *const
case 'R': // foo *volatile
case 'S': // foo *const volatile
return true;
}
return false;
}
static bool isArrayType(StringView S) { return S[0] == 'Y'; }
static bool isFunctionType(StringView S) {
return S.startsWith("$$A8@@") || S.startsWith("$$A6");
}
static FunctionRefQualifier
demangleFunctionRefQualifier(StringView &MangledName) {
if (MangledName.consumeFront('G'))
return FunctionRefQualifier::Reference;
else if (MangledName.consumeFront('H'))
return FunctionRefQualifier::RValueReference;
return FunctionRefQualifier::None;
}
static std::pair<Qualifiers, PointerAffinity>
demanglePointerCVQualifiers(StringView &MangledName) {
if (MangledName.consumeFront("$$Q"))
return std::make_pair(Q_None, PointerAffinity::RValueReference);
switch (MangledName.popFront()) {
case 'A':
return std::make_pair(Q_None, PointerAffinity::Reference);
case 'P':
return std::make_pair(Q_None, PointerAffinity::Pointer);
case 'Q':
return std::make_pair(Q_Const, PointerAffinity::Pointer);
case 'R':
return std::make_pair(Q_Volatile, PointerAffinity::Pointer);
case 'S':
return std::make_pair(Qualifiers(Q_Const | Q_Volatile),
PointerAffinity::Pointer);
}
// This function is only called if isPointerType() returns true,
// and it only returns true for the six cases listed above.
DEMANGLE_UNREACHABLE;
}
StringView Demangler::copyString(StringView Borrowed) {
char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1);
std::strcpy(Stable, Borrowed.begin());
return {Stable, Borrowed.size()};
}
SpecialTableSymbolNode *
Demangler::demangleSpecialTableSymbolNode(StringView &MangledName,
SpecialIntrinsicKind K) {
NamedIdentifierNode *NI = Arena.alloc<NamedIdentifierNode>();
switch (K) {
case SpecialIntrinsicKind::Vftable:
NI->Name = "`vftable'";
break;
case SpecialIntrinsicKind::Vbtable:
NI->Name = "`vbtable'";
break;
case SpecialIntrinsicKind::LocalVftable:
NI->Name = "`local vftable'";
break;
case SpecialIntrinsicKind::RttiCompleteObjLocator:
NI->Name = "`RTTI Complete Object Locator'";
break;
default:
DEMANGLE_UNREACHABLE;
}
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);
SpecialTableSymbolNode *STSN = Arena.alloc<SpecialTableSymbolNode>();
STSN->Name = QN;
bool IsMember = false;
if (MangledName.empty()) {
Error = true;
return nullptr;
}
char Front = MangledName.popFront();
if (Front != '6' && Front != '7') {
Error = true;
return nullptr;
}
std::tie(STSN->Quals, IsMember) = demangleQualifiers(MangledName);
if (!MangledName.consumeFront('@'))
STSN->TargetName = demangleFullyQualifiedTypeName(MangledName);
return STSN;
}
LocalStaticGuardVariableNode *
Demangler::demangleLocalStaticGuard(StringView &MangledName, bool IsThread) {
LocalStaticGuardIdentifierNode *LSGI =
Arena.alloc<LocalStaticGuardIdentifierNode>();
LSGI->IsThread = IsThread;
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, LSGI);
LocalStaticGuardVariableNode *LSGVN =
Arena.alloc<LocalStaticGuardVariableNode>();
LSGVN->Name = QN;
if (MangledName.consumeFront("4IA"))
LSGVN->IsVisible = false;
else if (MangledName.consumeFront("5"))
LSGVN->IsVisible = true;
else {
Error = true;
return nullptr;
}
if (!MangledName.empty())
LSGI->ScopeIndex = demangleUnsigned(MangledName);
return LSGVN;
}
static NamedIdentifierNode *synthesizeNamedIdentifier(ArenaAllocator &Arena,
StringView Name) {
NamedIdentifierNode *Id = Arena.alloc<NamedIdentifierNode>();
Id->Name = Name;
return Id;
}
static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,
IdentifierNode *Identifier) {
QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();
QN->Components = Arena.alloc<NodeArrayNode>();
QN->Components->Count = 1;
QN->Components->Nodes = Arena.allocArray<Node *>(1);
QN->Components->Nodes[0] = Identifier;
return QN;
}
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,
StringView Name) {
NamedIdentifierNode *Id = synthesizeNamedIdentifier(Arena, Name);
return synthesizeQualifiedName(Arena, Id);
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
}
static VariableSymbolNode *synthesizeVariable(ArenaAllocator &Arena,
TypeNode *Type,
StringView VariableName) {
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
VSN->Type = Type;
VSN->Name = synthesizeQualifiedName(Arena, VariableName);
return VSN;
}
VariableSymbolNode *Demangler::demangleUntypedVariable(
ArenaAllocator &Arena, StringView &MangledName, StringView VariableName) {
NamedIdentifierNode *NI = synthesizeNamedIdentifier(Arena, VariableName);
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
VSN->Name = QN;
if (MangledName.consumeFront("8"))
return VSN;
Error = true;
return nullptr;
}
VariableSymbolNode *
Demangler::demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena,
StringView &MangledName) {
RttiBaseClassDescriptorNode *RBCDN =
Arena.alloc<RttiBaseClassDescriptorNode>();
RBCDN->NVOffset = demangleUnsigned(MangledName);
RBCDN->VBPtrOffset = demangleSigned(MangledName);
RBCDN->VBTableOffset = demangleUnsigned(MangledName);
RBCDN->Flags = demangleUnsigned(MangledName);
if (Error)
return nullptr;
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
VSN->Name = demangleNameScopeChain(MangledName, RBCDN);
MangledName.consumeFront('8');
return VSN;
}
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
FunctionSymbolNode *Demangler::demangleInitFiniStub(StringView &MangledName,
bool IsDestructor) {
DynamicStructorIdentifierNode *DSIN =
Arena.alloc<DynamicStructorIdentifierNode>();
DSIN->IsDestructor = IsDestructor;
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
bool IsKnownStaticDataMember = false;
if (MangledName.consumeFront('?'))
IsKnownStaticDataMember = true;
SymbolNode *Symbol = demangleDeclarator(MangledName);
if (Error)
return nullptr;
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
FunctionSymbolNode *FSN = nullptr;
if (Symbol->kind() == NodeKind::VariableSymbol) {
DSIN->Variable = static_cast<VariableSymbolNode *>(Symbol);
// Older versions of clang mangled this type of symbol incorrectly. They
// would omit the leading ? and they would only emit a single @ at the end.
// The correct mangling is a leading ? and 2 trailing @ signs. Handle
// both cases.
int AtCount = IsKnownStaticDataMember ? 2 : 1;
for (int I = 0; I < AtCount; ++I) {
if (MangledName.consumeFront('@'))
continue;
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
Error = true;
return nullptr;
}
FSN = demangleFunctionEncoding(MangledName);
if (FSN)
FSN->Name = synthesizeQualifiedName(Arena, DSIN);
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
} else {
if (IsKnownStaticDataMember) {
// This was supposed to be a static data member, but we got a function.
Error = true;
return nullptr;
}
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
FSN = static_cast<FunctionSymbolNode *>(Symbol);
DSIN->Name = Symbol->Name;
FSN->Name = synthesizeQualifiedName(Arena, DSIN);
}
return FSN;
}
SymbolNode *Demangler::demangleSpecialIntrinsic(StringView &MangledName) {
SpecialIntrinsicKind SIK = consumeSpecialIntrinsicKind(MangledName);
switch (SIK) {
case SpecialIntrinsicKind::None:
return nullptr;
case SpecialIntrinsicKind::StringLiteralSymbol:
return demangleStringLiteral(MangledName);
case SpecialIntrinsicKind::Vftable:
case SpecialIntrinsicKind::Vbtable:
case SpecialIntrinsicKind::LocalVftable:
case SpecialIntrinsicKind::RttiCompleteObjLocator:
return demangleSpecialTableSymbolNode(MangledName, SIK);
case SpecialIntrinsicKind::VcallThunk:
return demangleVcallThunkNode(MangledName);
case SpecialIntrinsicKind::LocalStaticGuard:
return demangleLocalStaticGuard(MangledName, /*IsThread=*/false);
case SpecialIntrinsicKind::LocalStaticThreadGuard:
return demangleLocalStaticGuard(MangledName, /*IsThread=*/true);
case SpecialIntrinsicKind::RttiTypeDescriptor: {
TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);
if (Error)
break;
if (!MangledName.consumeFront("@8"))
break;
if (!MangledName.empty())
break;
return synthesizeVariable(Arena, T, "`RTTI Type Descriptor'");
}
case SpecialIntrinsicKind::RttiBaseClassArray:
return demangleUntypedVariable(Arena, MangledName,
"`RTTI Base Class Array'");
case SpecialIntrinsicKind::RttiClassHierarchyDescriptor:
return demangleUntypedVariable(Arena, MangledName,
"`RTTI Class Hierarchy Descriptor'");
case SpecialIntrinsicKind::RttiBaseClassDescriptor:
return demangleRttiBaseClassDescriptorNode(Arena, MangledName);
case SpecialIntrinsicKind::DynamicInitializer:
return demangleInitFiniStub(MangledName, /*IsDestructor=*/false);
case SpecialIntrinsicKind::DynamicAtexitDestructor:
return demangleInitFiniStub(MangledName, /*IsDestructor=*/true);
case SpecialIntrinsicKind::Typeof:
case SpecialIntrinsicKind::UdtReturning:
// It's unclear which tools produces these manglings, so demangling
// support is not (yet?) implemented.
break;
case SpecialIntrinsicKind::Unknown:
DEMANGLE_UNREACHABLE; // Never returned by consumeSpecialIntrinsicKind.
}
Error = true;
return nullptr;
}
IdentifierNode *
Demangler::demangleFunctionIdentifierCode(StringView &MangledName) {
assert(MangledName.startsWith('?'));
MangledName = MangledName.dropFront();
if (MangledName.empty()) {
Error = true;
return nullptr;
}
if (MangledName.consumeFront("__"))
return demangleFunctionIdentifierCode(
MangledName, FunctionIdentifierCodeGroup::DoubleUnder);
if (MangledName.consumeFront("_"))
return demangleFunctionIdentifierCode(MangledName,
FunctionIdentifierCodeGroup::Under);
return demangleFunctionIdentifierCode(MangledName,
FunctionIdentifierCodeGroup::Basic);
}
StructorIdentifierNode *
Demangler::demangleStructorIdentifier(StringView &MangledName,
bool IsDestructor) {
StructorIdentifierNode *N = Arena.alloc<StructorIdentifierNode>();
N->IsDestructor = IsDestructor;
return N;
}
ConversionOperatorIdentifierNode *
Demangler::demangleConversionOperatorIdentifier(StringView &MangledName) {
ConversionOperatorIdentifierNode *N =
Arena.alloc<ConversionOperatorIdentifierNode>();
return N;
}
LiteralOperatorIdentifierNode *
Demangler::demangleLiteralOperatorIdentifier(StringView &MangledName) {
LiteralOperatorIdentifierNode *N =
Arena.alloc<LiteralOperatorIdentifierNode>();
N->Name = demangleSimpleString(MangledName, /*Memorize=*/false);
return N;
}
IntrinsicFunctionKind
Demangler::translateIntrinsicFunctionCode(char CH,
FunctionIdentifierCodeGroup Group) {
using IFK = IntrinsicFunctionKind;
if (!(CH >= '0' && CH <= '9') && !(CH >= 'A' && CH <= 'Z')) {
Error = true;
return IFK::None;
}
// Not all ? identifiers are intrinsics *functions*. This function only maps
// operator codes for the special functions, all others are handled elsewhere,
// hence the IFK::None entries in the table.
static IFK Basic[36] = {
IFK::None, // ?0 # Foo::Foo()
IFK::None, // ?1 # Foo::~Foo()
IFK::New, // ?2 # operator new
IFK::Delete, // ?3 # operator delete
IFK::Assign, // ?4 # operator=
IFK::RightShift, // ?5 # operator>>
IFK::LeftShift, // ?6 # operator<<
IFK::LogicalNot, // ?7 # operator!
IFK::Equals, // ?8 # operator==
IFK::NotEquals, // ?9 # operator!=
IFK::ArraySubscript, // ?A # operator[]
IFK::None, // ?B # Foo::operator <type>()
IFK::Pointer, // ?C # operator->
IFK::Dereference, // ?D # operator*
IFK::Increment, // ?E # operator++
IFK::Decrement, // ?F # operator--
IFK::Minus, // ?G # operator-
IFK::Plus, // ?H # operator+
IFK::BitwiseAnd, // ?I # operator&
IFK::MemberPointer, // ?J # operator->*
IFK::Divide, // ?K # operator/
IFK::Modulus, // ?L # operator%
IFK::LessThan, // ?M operator<
IFK::LessThanEqual, // ?N operator<=
IFK::GreaterThan, // ?O operator>
IFK::GreaterThanEqual, // ?P operator>=
IFK::Comma, // ?Q operator,
IFK::Parens, // ?R operator()
IFK::BitwiseNot, // ?S operator~
IFK::BitwiseXor, // ?T operator^
IFK::BitwiseOr, // ?U operator|
IFK::LogicalAnd, // ?V operator&&
IFK::LogicalOr, // ?W operator||
IFK::TimesEqual, // ?X operator*=
IFK::PlusEqual, // ?Y operator+=
IFK::MinusEqual, // ?Z operator-=
};
static IFK Under[36] = {
IFK::DivEqual, // ?_0 operator/=
IFK::ModEqual, // ?_1 operator%=
IFK::RshEqual, // ?_2 operator>>=
IFK::LshEqual, // ?_3 operator<<=
IFK::BitwiseAndEqual, // ?_4 operator&=
IFK::BitwiseOrEqual, // ?_5 operator|=
IFK::BitwiseXorEqual, // ?_6 operator^=
IFK::None, // ?_7 # vftable
IFK::None, // ?_8 # vbtable
IFK::None, // ?_9 # vcall
IFK::None, // ?_A # typeof
IFK::None, // ?_B # local static guard
IFK::None, // ?_C # string literal
IFK::VbaseDtor, // ?_D # vbase destructor
IFK::VecDelDtor, // ?_E # vector deleting destructor
IFK::DefaultCtorClosure, // ?_F # default constructor closure
IFK::ScalarDelDtor, // ?_G # scalar deleting destructor
IFK::VecCtorIter, // ?_H # vector constructor iterator
IFK::VecDtorIter, // ?_I # vector destructor iterator
IFK::VecVbaseCtorIter, // ?_J # vector vbase constructor iterator
IFK::VdispMap, // ?_K # virtual displacement map
IFK::EHVecCtorIter, // ?_L # eh vector constructor iterator
IFK::EHVecDtorIter, // ?_M # eh vector destructor iterator
IFK::EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator
IFK::CopyCtorClosure, // ?_O # copy constructor closure
IFK::None, // ?_P<name> # udt returning <name>
IFK::None, // ?_Q # <unknown>
IFK::None, // ?_R0 - ?_R4 # RTTI Codes
IFK::None, // ?_S # local vftable
IFK::LocalVftableCtorClosure, // ?_T # local vftable constructor closure
IFK::ArrayNew, // ?_U operator new[]
IFK::ArrayDelete, // ?_V operator delete[]
IFK::None, // ?_W <unused>
IFK::None, // ?_X <unused>
IFK::None, // ?_Y <unused>
IFK::None, // ?_Z <unused>
};
static IFK DoubleUnder[36] = {
IFK::None, // ?__0 <unused>
IFK::None, // ?__1 <unused>
IFK::None, // ?__2 <unused>
IFK::None, // ?__3 <unused>
IFK::None, // ?__4 <unused>
IFK::None, // ?__5 <unused>
IFK::None, // ?__6 <unused>
IFK::None, // ?__7 <unused>
IFK::None, // ?__8 <unused>
IFK::None, // ?__9 <unused>
IFK::ManVectorCtorIter, // ?__A managed vector ctor iterator
IFK::ManVectorDtorIter, // ?__B managed vector dtor iterator
IFK::EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator
IFK::EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iter
IFK::None, // ?__E dynamic initializer for `T'
IFK::None, // ?__F dynamic atexit destructor for `T'
IFK::VectorCopyCtorIter, // ?__G vector copy constructor iter
IFK::VectorVbaseCopyCtorIter, // ?__H vector vbase copy ctor iter
IFK::ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy ctor
// iter
IFK::None, // ?__J local static thread guard
IFK::None, // ?__K operator ""_name
IFK::CoAwait, // ?__L operator co_await
IFK::Spaceship, // ?__M operator<=>
IFK::None, // ?__N <unused>
IFK::None, // ?__O <unused>
IFK::None, // ?__P <unused>
IFK::None, // ?__Q <unused>
IFK::None, // ?__R <unused>
IFK::None, // ?__S <unused>
IFK::None, // ?__T <unused>
IFK::None, // ?__U <unused>
IFK::None, // ?__V <unused>
IFK::None, // ?__W <unused>
IFK::None, // ?__X <unused>
IFK::None, // ?__Y <unused>
IFK::None, // ?__Z <unused>
};
int Index = (CH >= '0' && CH <= '9') ? (CH - '0') : (CH - 'A' + 10);
switch (Group) {
case FunctionIdentifierCodeGroup::Basic:
return Basic[Index];
case FunctionIdentifierCodeGroup::Under:
return Under[Index];
case FunctionIdentifierCodeGroup::DoubleUnder:
return DoubleUnder[Index];
}
DEMANGLE_UNREACHABLE;
}
IdentifierNode *
Demangler::demangleFunctionIdentifierCode(StringView &MangledName,
FunctionIdentifierCodeGroup Group) {
if (MangledName.empty()) {
Error = true;
return nullptr;
}
switch (Group) {
case FunctionIdentifierCodeGroup::Basic:
switch (char CH = MangledName.popFront()) {
case '0':
case '1':
return demangleStructorIdentifier(MangledName, CH == '1');
case 'B':
return demangleConversionOperatorIdentifier(MangledName);
default:
return Arena.alloc<IntrinsicFunctionIdentifierNode>(
translateIntrinsicFunctionCode(CH, Group));
}
case FunctionIdentifierCodeGroup::Under:
return Arena.alloc<IntrinsicFunctionIdentifierNode>(
translateIntrinsicFunctionCode(MangledName.popFront(), Group));
case FunctionIdentifierCodeGroup::DoubleUnder:
switch (char CH = MangledName.popFront()) {
case 'K':
return demangleLiteralOperatorIdentifier(MangledName);
default:
return Arena.alloc<IntrinsicFunctionIdentifierNode>(
translateIntrinsicFunctionCode(CH, Group));
}
}
DEMANGLE_UNREACHABLE;
}
SymbolNode *Demangler::demangleEncodedSymbol(StringView &MangledName,
QualifiedNameNode *Name) {
if (MangledName.empty()) {
Error = true;
return nullptr;
}
// Read a variable.
switch (MangledName.front()) {
case '0':
case '1':
case '2':
case '3':
case '4': {
StorageClass SC = demangleVariableStorageClass(MangledName);
return demangleVariableEncoding(MangledName, SC);
}
}
FunctionSymbolNode *FSN = demangleFunctionEncoding(MangledName);
IdentifierNode *UQN = Name->getUnqualifiedIdentifier();
if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {
ConversionOperatorIdentifierNode *COIN =
static_cast<ConversionOperatorIdentifierNode *>(UQN);
if (FSN)
COIN->TargetType = FSN->Signature->ReturnType;
}
return FSN;
}
SymbolNode *Demangler::demangleDeclarator(StringView &MangledName) {
// What follows is a main symbol name. This may include namespaces or class
// back references.
QualifiedNameNode *QN = demangleFullyQualifiedSymbolName(MangledName);
if (Error)
return nullptr;
SymbolNode *Symbol = demangleEncodedSymbol(MangledName, QN);
if (Error)
return nullptr;
Symbol->Name = QN;
IdentifierNode *UQN = QN->getUnqualifiedIdentifier();
if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {
ConversionOperatorIdentifierNode *COIN =
static_cast<ConversionOperatorIdentifierNode *>(UQN);
if (!COIN->TargetType) {
Error = true;
return nullptr;
}
}
return Symbol;
}
SymbolNode *Demangler::demangleMD5Name(StringView &MangledName) {
assert(MangledName.startsWith("??@"));
// This is an MD5 mangled name. We can't demangle it, just return the
// mangled name.
// An MD5 mangled name is ??@ followed by 32 characters and a terminating @.
size_t MD5Last = MangledName.find('@', strlen("??@"));
if (MD5Last == StringView::npos) {
Error = true;
return nullptr;
}
const char *Start = MangledName.begin();
MangledName = MangledName.dropFront(MD5Last + 1);
// There are two additional special cases for MD5 names:
// 1. For complete object locators where the object name is long enough
// for the object to have an MD5 name, the complete object locator is
// called ??@...@??_R4@ (with a trailing "??_R4@" instead of the usual
// leading "??_R4". This is handled here.
// 2. For catchable types, in versions of MSVC before 2015 (<1900) or after
// 2017.2 (>= 1914), the catchable type mangling is _CT??@...@??@...@8
// instead of_CT??@...@8 with just one MD5 name. Since we don't yet
// demangle catchable types anywhere, this isn't handled for MD5 names
// either.
MangledName.consumeFront("??_R4@");
StringView MD5(Start, MangledName.begin());
SymbolNode *S = Arena.alloc<SymbolNode>(NodeKind::Md5Symbol);
S->Name = synthesizeQualifiedName(Arena, MD5);
return S;
}
SymbolNode *Demangler::demangleTypeinfoName(StringView &MangledName) {
assert(MangledName.startsWith('.'));
MangledName.consumeFront('.');
TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);
if (Error || !MangledName.empty()) {
Error = true;
return nullptr;
}
return synthesizeVariable(Arena, T, "`RTTI Type Descriptor Name'");
}
// Parser entry point.
SymbolNode *Demangler::parse(StringView &MangledName) {
// Typeinfo names are strings stored in RTTI data. They're not symbol names.
// It's still useful to demangle them. They're the only demangled entity
// that doesn't start with a "?" but a ".".
if (MangledName.startsWith('.'))
return demangleTypeinfoName(MangledName);
if (MangledName.startsWith("??@"))
return demangleMD5Name(MangledName);
// MSVC-style mangled symbols must start with '?'.
if (!MangledName.startsWith('?')) {
Error = true;
return nullptr;
}
MangledName.consumeFront('?');
// ?$ is a template instantiation, but all other names that start with ? are
// operators / special names.
if (SymbolNode *SI = demangleSpecialIntrinsic(MangledName))
return SI;
return demangleDeclarator(MangledName);
}
TagTypeNode *Demangler::parseTagUniqueName(StringView &MangledName) {
if (!MangledName.consumeFront(".?A"))
return nullptr;
MangledName.consumeFront(".?A");
if (MangledName.empty())
return nullptr;
return demangleClassType(MangledName);
}
// <type-encoding> ::= <storage-class> <variable-type>
// <storage-class> ::= 0 # private static member
// ::= 1 # protected static member
// ::= 2 # public static member
// ::= 3 # global
// ::= 4 # static local
VariableSymbolNode *Demangler::demangleVariableEncoding(StringView &MangledName,
StorageClass SC) {
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
VSN->Type = demangleType(MangledName, QualifierMangleMode::Drop);
VSN->SC = SC;
if (Error)
return nullptr;
// <variable-type> ::= <type> <cvr-qualifiers>
// ::= <type> <pointee-cvr-qualifiers> # pointers, references
switch (VSN->Type->kind()) {
case NodeKind::PointerType: {
PointerTypeNode *PTN = static_cast<PointerTypeNode *>(VSN->Type);
Qualifiers ExtraChildQuals = Q_None;
PTN->Quals = Qualifiers(VSN->Type->Quals |
demanglePointerExtQualifiers(MangledName));
bool IsMember = false;
std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName);
if (PTN->ClassParent) {
QualifiedNameNode *BackRefName =
demangleFullyQualifiedTypeName(MangledName);
(void)BackRefName;
}
PTN->Pointee->Quals = Qualifiers(PTN->Pointee->Quals | ExtraChildQuals);
break;
}
default:
VSN->Type->Quals = demangleQualifiers(MangledName).first;
break;
}
return VSN;
}
// Sometimes numbers are encoded in mangled symbols. For example,
// "int (*x)[20]" is a valid C type (x is a pointer to an array of
// length 20), so we need some way to embed numbers as part of symbols.
// This function parses it.
//
// <number> ::= [?] <non-negative integer>
//
// <non-negative integer> ::= <decimal digit> # when 1 <= Number <= 10
// ::= <hex digit>+ @ # when Number == 0 or >= 10
//
// <hex-digit> ::= [A-P] # A = 0, B = 1, ...
std::pair<uint64_t, bool> Demangler::demangleNumber(StringView &MangledName) {
bool IsNegative = MangledName.consumeFront('?');
if (startsWithDigit(MangledName)) {
uint64_t Ret = MangledName[0] - '0' + 1;
MangledName = MangledName.dropFront(1);
return {Ret, IsNegative};
}
uint64_t Ret = 0;
for (size_t i = 0; i < MangledName.size(); ++i) {
char C = MangledName[i];
if (C == '@') {
MangledName = MangledName.dropFront(i + 1);
return {Ret, IsNegative};
}
if ('A' <= C && C <= 'P') {
Ret = (Ret << 4) + (C - 'A');
continue;
}
break;
}
Error = true;
return {0ULL, false};
}
uint64_t Demangler::demangleUnsigned(StringView &MangledName) {
bool IsNegative = false;
uint64_t Number = 0;
std::tie(Number, IsNegative) = demangleNumber(MangledName);
if (IsNegative)
Error = true;
return Number;
}
int64_t Demangler::demangleSigned(StringView &MangledName) {
bool IsNegative = false;
uint64_t Number = 0;
std::tie(Number, IsNegative) = demangleNumber(MangledName);
if (Number > INT64_MAX)
Error = true;
int64_t I = static_cast<int64_t>(Number);
return IsNegative ? -I : I;
}
// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9.
// Memorize it.
void Demangler::memorizeString(StringView S) {
if (Backrefs.NamesCount >= BackrefContext::Max)
return;
for (size_t i = 0; i < Backrefs.NamesCount; ++i)
if (S == Backrefs.Names[i]->Name)
return;
NamedIdentifierNode *N = Arena.alloc<NamedIdentifierNode>();
N->Name = S;
Backrefs.Names[Backrefs.NamesCount++] = N;
}
NamedIdentifierNode *Demangler::demangleBackRefName(StringView &MangledName) {
assert(startsWithDigit(MangledName));
size_t I = MangledName[0] - '0';
if (I >= Backrefs.NamesCount) {
Error = true;
return nullptr;
}
MangledName = MangledName.dropFront();
return Backrefs.Names[I];
}
void Demangler::memorizeIdentifier(IdentifierNode *Identifier) {
// Render this class template name into a string buffer so that we can
// memorize it for the purpose of back-referencing.
OutputStream OS;
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
// FIXME: Propagate out-of-memory as an error?
std::terminate();
Identifier->output(OS, OF_Default);
OS << '\0';
char *Name = OS.getBuffer();
StringView Owned = copyString(Name);
memorizeString(Owned);
std::free(Name);
}
IdentifierNode *
Demangler::demangleTemplateInstantiationName(StringView &MangledName,
NameBackrefBehavior NBB) {
assert(MangledName.startsWith("?$"));
MangledName.consumeFront("?$");
BackrefContext OuterContext;
std::swap(OuterContext, Backrefs);
IdentifierNode *Identifier =
demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
if (!Error)
Identifier->TemplateParams = demangleTemplateParameterList(MangledName);
std::swap(OuterContext, Backrefs);
if (Error)
return nullptr;
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
if (NBB & NBB_Template) {
// NBB_Template is only set for types and non-leaf names ("a::" in "a::b").
// Structors and conversion operators only makes sense in a leaf name, so
// reject them in NBB_Template contexts.
if (Identifier->kind() == NodeKind::ConversionOperatorIdentifier ||
Identifier->kind() == NodeKind::StructorIdentifier) {
Error = true;
return nullptr;
}
memorizeIdentifier(Identifier);
}
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
return Identifier;
}
NamedIdentifierNode *Demangler::demangleSimpleName(StringView &MangledName,
bool Memorize) {
StringView S = demangleSimpleString(MangledName, Memorize);
if (Error)
return nullptr;
NamedIdentifierNode *Name = Arena.alloc<NamedIdentifierNode>();
Name->Name = S;
return Name;
}
static bool isRebasedHexDigit(char C) { return (C >= 'A' && C <= 'P'); }
static uint8_t rebasedHexDigitToNumber(char C) {
assert(isRebasedHexDigit(C));
return (C <= 'J') ? (C - 'A') : (10 + C - 'K');
}
uint8_t Demangler::demangleCharLiteral(StringView &MangledName) {
assert(!MangledName.empty());
if (!MangledName.startsWith('?'))
return MangledName.popFront();
MangledName = MangledName.dropFront();
if (MangledName.empty())
goto CharLiteralError;
if (MangledName.consumeFront('$')) {
// Two hex digits
if (MangledName.size() < 2)
goto CharLiteralError;
StringView Nibbles = MangledName.substr(0, 2);
if (!isRebasedHexDigit(Nibbles[0]) || !isRebasedHexDigit(Nibbles[1]))
goto CharLiteralError;
// Don't append the null terminator.
uint8_t C1 = rebasedHexDigitToNumber(Nibbles[0]);
uint8_t C2 = rebasedHexDigitToNumber(Nibbles[1]);
MangledName = MangledName.dropFront(2);
return (C1 << 4) | C2;
}
if (startsWithDigit(MangledName)) {
const char *Lookup = ",/\\:. \n\t'-";
char C = Lookup[MangledName[0] - '0'];
MangledName = MangledName.dropFront();
return C;
}
if (MangledName[0] >= 'a' && MangledName[0] <= 'z') {
char Lookup[26] = {'\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7',
'\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE',
'\xEF', '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5',
'\xF6', '\xF7', '\xF8', '\xF9', '\xFA'};
char C = Lookup[MangledName[0] - 'a'];
MangledName = MangledName.dropFront();
return C;
}
if (MangledName[0] >= 'A' && MangledName[0] <= 'Z') {
char Lookup[26] = {'\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7',
'\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE',
'\xCF', '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5',
'\xD6', '\xD7', '\xD8', '\xD9', '\xDA'};
char C = Lookup[MangledName[0] - 'A'];
MangledName = MangledName.dropFront();
return C;
}
CharLiteralError:
Error = true;
return '\0';
}
wchar_t Demangler::demangleWcharLiteral(StringView &MangledName) {
uint8_t C1, C2;
C1 = demangleCharLiteral(MangledName);
if (Error || MangledName.empty())
goto WCharLiteralError;
C2 = demangleCharLiteral(MangledName);
if (Error)
goto WCharLiteralError;
return ((wchar_t)C1 << 8) | (wchar_t)C2;
WCharLiteralError:
Error = true;
return L'\0';
}
static void writeHexDigit(char *Buffer, uint8_t Digit) {
assert(Digit <= 15);
*Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10);
}
static void outputHex(OutputStream &OS, unsigned C) {
assert (C != 0);
// It's easier to do the math if we can work from right to left, but we need
// to print the numbers from left to right. So render this into a temporary
// buffer first, then output the temporary buffer. Each byte is of the form
// \xAB, which means that each byte needs 4 characters. Since there are at
// most 4 bytes, we need a 4*4+1 = 17 character temporary buffer.
char TempBuffer[17];
::memset(TempBuffer, 0, sizeof(TempBuffer));
constexpr int MaxPos = sizeof(TempBuffer) - 1;
int Pos = MaxPos - 1; // TempBuffer[MaxPos] is the terminating \0.
while (C != 0) {
for (int I = 0; I < 2; ++I) {
writeHexDigit(&TempBuffer[Pos--], C % 16);
C /= 16;
}
}
TempBuffer[Pos--] = 'x';
assert(Pos >= 0);
TempBuffer[Pos--] = '\\';
OS << StringView(&TempBuffer[Pos + 1]);
}
static void outputEscapedChar(OutputStream &OS, unsigned C) {
switch (C) {
case '\0': // nul
OS << "\\0";
return;
case '\'': // single quote
OS << "\\\'";
return;
case '\"': // double quote
OS << "\\\"";
return;
case '\\': // backslash
OS << "\\\\";
return;
case '\a': // bell
OS << "\\a";
return;
case '\b': // backspace
OS << "\\b";
return;
case '\f': // form feed
OS << "\\f";
return;
case '\n': // new line
OS << "\\n";
return;
case '\r': // carriage return
OS << "\\r";
return;
case '\t': // tab
OS << "\\t";
return;
case '\v': // vertical tab
OS << "\\v";
return;
default:
break;
}
if (C > 0x1F && C < 0x7F) {
// Standard ascii char.
OS << (char)C;
return;
}
outputHex(OS, C);
}
static unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) {
const uint8_t *End = StringBytes + Length - 1;
unsigned Count = 0;
while (Length > 0 && *End == 0) {
--Length;
--End;
++Count;
}
return Count;
}
static unsigned countEmbeddedNulls(const uint8_t *StringBytes,
unsigned Length) {
unsigned Result = 0;
for (unsigned I = 0; I < Length; ++I) {
if (*StringBytes++ == 0)
++Result;
}
return Result;
}
// A mangled (non-wide) string literal stores the total length of the string it
// refers to (passed in NumBytes), and it contains up to 32 bytes of actual text
// (passed in StringBytes, NumChars).
static unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars,
uint64_t NumBytes) {
assert(NumBytes > 0);
// If the number of bytes is odd, this is guaranteed to be a char string.
if (NumBytes % 2 == 1)
return 1;
// All strings can encode at most 32 bytes of data. If it's less than that,
// then we encoded the entire string. In this case we check for a 1-byte,
// 2-byte, or 4-byte null terminator.
if (NumBytes < 32) {
unsigned TrailingNulls = countTrailingNullBytes(StringBytes, NumChars);
if (TrailingNulls >= 4 && NumBytes % 4 == 0)
return 4;
if (TrailingNulls >= 2)
return 2;
return 1;
}
// The whole string was not able to be encoded. Try to look at embedded null
// terminators to guess. The heuristic is that we count all embedded null
// terminators. If more than 2/3 are null, it's a char32. If more than 1/3
// are null, it's a char16. Otherwise it's a char8. This obviously isn't
// perfect and is biased towards languages that have ascii alphabets, but this
// was always going to be best effort since the encoding is lossy.
unsigned Nulls = countEmbeddedNulls(StringBytes, NumChars);
if (Nulls >= 2 * NumChars / 3 && NumBytes % 4 == 0)
return 4;
if (Nulls >= NumChars / 3)
return 2;
return 1;
}
static unsigned decodeMultiByteChar(const uint8_t *StringBytes,
unsigned CharIndex, unsigned CharBytes) {
assert(CharBytes == 1 || CharBytes == 2 || CharBytes == 4);
unsigned Offset = CharIndex * CharBytes;
unsigned Result = 0;
StringBytes = StringBytes + Offset;
for (unsigned I = 0; I < CharBytes; ++I) {
unsigned C = static_cast<unsigned>(StringBytes[I]);
Result |= C << (8 * I);
}
return Result;
}
FunctionSymbolNode *Demangler::demangleVcallThunkNode(StringView &MangledName) {
FunctionSymbolNode *FSN = Arena.alloc<FunctionSymbolNode>();
VcallThunkIdentifierNode *VTIN = Arena.alloc<VcallThunkIdentifierNode>();
FSN->Signature = Arena.alloc<ThunkSignatureNode>();
FSN->Signature->FunctionClass = FC_NoParameterList;
FSN->Name = demangleNameScopeChain(MangledName, VTIN);
if (!Error)
Error = !MangledName.consumeFront("$B");
if (!Error)
VTIN->OffsetInVTable = demangleUnsigned(MangledName);
if (!Error)
Error = !MangledName.consumeFront('A');
if (!Error)
FSN->Signature->CallConvention = demangleCallingConvention(MangledName);
return (Error) ? nullptr : FSN;
}
EncodedStringLiteralNode *
Demangler::demangleStringLiteral(StringView &MangledName) {
// This function uses goto, so declare all variables up front.
OutputStream OS;
StringView CRC;
uint64_t StringByteSize;
bool IsWcharT = false;
bool IsNegative = false;
size_t CrcEndPos = 0;
char *ResultBuffer = nullptr;
EncodedStringLiteralNode *Result = Arena.alloc<EncodedStringLiteralNode>();
// Must happen before the first `goto StringLiteralError`.
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
// FIXME: Propagate out-of-memory as an error?
std::terminate();
// Prefix indicating the beginning of a string literal
if (!MangledName.consumeFront("@_"))
goto StringLiteralError;
if (MangledName.empty())
goto StringLiteralError;
// Char Type (regular or wchar_t)
switch (MangledName.popFront()) {
case '1':
IsWcharT = true;
DEMANGLE_FALLTHROUGH;
case '0':
break;
default:
goto StringLiteralError;
}
// Encoded Length
std::tie(StringByteSize, IsNegative) = demangleNumber(MangledName);
if (Error || IsNegative || StringByteSize < (IsWcharT ? 2 : 1))
goto StringLiteralError;
// CRC 32 (always 8 characters plus a terminator)
CrcEndPos = MangledName.find('@');
if (CrcEndPos == StringView::npos)
goto StringLiteralError;
CRC = MangledName.substr(0, CrcEndPos);
MangledName = MangledName.dropFront(CrcEndPos + 1);
if (MangledName.empty())
goto StringLiteralError;
if (IsWcharT) {
Result->Char = CharKind::Wchar;
if (StringByteSize > 64)
Result->IsTruncated = true;
while (!MangledName.consumeFront('@')) {
if (MangledName.size() < 2)
goto StringLiteralError;
wchar_t W = demangleWcharLiteral(MangledName);
if (StringByteSize != 2 || Result->IsTruncated)
outputEscapedChar(OS, W);
StringByteSize -= 2;
if (Error)
goto StringLiteralError;
}
} else {
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
// The max byte length is actually 32, but some compilers mangled strings
// incorrectly, so we have to assume it can go higher.
constexpr unsigned MaxStringByteLength = 32 * 4;
uint8_t StringBytes[MaxStringByteLength];
unsigned BytesDecoded = 0;
while (!MangledName.consumeFront('@')) {
if (MangledName.size() < 1 || BytesDecoded >= MaxStringByteLength)
goto StringLiteralError;
StringBytes[BytesDecoded++] = demangleCharLiteral(MangledName);
}
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
if (StringByteSize > BytesDecoded)
Result->IsTruncated = true;
unsigned CharBytes =
guessCharByteSize(StringBytes, BytesDecoded, StringByteSize);
assert(StringByteSize % CharBytes == 0);
switch (CharBytes) {
case 1:
Result->Char = CharKind::Char;
break;
case 2:
Result->Char = CharKind::Char16;
break;
case 4:
Result->Char = CharKind::Char32;
break;
default:
DEMANGLE_UNREACHABLE;
}
const unsigned NumChars = BytesDecoded / CharBytes;
for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) {
unsigned NextChar =
decodeMultiByteChar(StringBytes, CharIndex, CharBytes);
if (CharIndex + 1 < NumChars || Result->IsTruncated)
outputEscapedChar(OS, NextChar);
}
}
OS << '\0';
ResultBuffer = OS.getBuffer();
Result->DecodedString = copyString(ResultBuffer);
std::free(ResultBuffer);
return Result;
StringLiteralError:
Error = true;
std::free(OS.getBuffer());
return nullptr;
}
// Returns MangledName's prefix before the first '@', or an error if
// MangledName contains no '@' or the prefix has length 0.
StringView Demangler::demangleSimpleString(StringView &MangledName,
bool Memorize) {
StringView S;
for (size_t i = 0; i < MangledName.size(); ++i) {
if (MangledName[i] != '@')
continue;
if (i == 0)
break;
S = MangledName.substr(0, i);
MangledName = MangledName.dropFront(i + 1);
if (Memorize)
memorizeString(S);
return S;
}
Error = true;
return {};
}
NamedIdentifierNode *
Demangler::demangleAnonymousNamespaceName(StringView &MangledName) {
assert(MangledName.startsWith("?A"));
MangledName.consumeFront("?A");
NamedIdentifierNode *Node = Arena.alloc<NamedIdentifierNode>();
Node->Name = "`anonymous namespace'";
size_t EndPos = MangledName.find('@');
if (EndPos == StringView::npos) {
Error = true;
return nullptr;
}
StringView NamespaceKey = MangledName.substr(0, EndPos);
memorizeString(NamespaceKey);
MangledName = MangledName.substr(EndPos + 1);
return Node;
}
NamedIdentifierNode *
Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) {
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
assert(startsWithLocalScopePattern(MangledName));
NamedIdentifierNode *Identifier = Arena.alloc<NamedIdentifierNode>();
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
MangledName.consumeFront('?');
uint64_t Number = 0;
bool IsNegative = false;
std::tie(Number, IsNegative) = demangleNumber(MangledName);
assert(!IsNegative);
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
// One ? to terminate the number
MangledName.consumeFront('?');
assert(!Error);
Node *Scope = parse(MangledName);
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
if (Error)
return nullptr;
// Render the parent symbol's name into a buffer.
OutputStream OS;
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
// FIXME: Propagate out-of-memory as an error?
std::terminate();
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
OS << '`';
Scope->output(OS, OF_Default);
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
OS << '\'';
OS << "::`" << Number << "'";
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
OS << '\0';
char *Result = OS.getBuffer();
Identifier->Name = copyString(Result);
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
std::free(Result);
return Identifier;
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
}
// Parses a type name in the form of A@B@C@@ which represents C::B::A.
QualifiedNameNode *
Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) {
IdentifierNode *Identifier =
demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true);
if (Error)
return nullptr;
assert(Identifier);
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);
if (Error)
return nullptr;
assert(QN);
return QN;
}
// Parses a symbol name in the form of A@B@C@@ which represents C::B::A.
// Symbol names have slightly different rules regarding what can appear
// so we separate out the implementations for flexibility.
QualifiedNameNode *
Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) {
// This is the final component of a symbol name (i.e. the leftmost component
// of a mangled name. Since the only possible template instantiation that
// can appear in this context is a function template, and since those are
// not saved for the purposes of name backreferences, only backref simple
// names.
IdentifierNode *Identifier =
demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
if (Error)
return nullptr;
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);
if (Error)
return nullptr;
if (Identifier->kind() == NodeKind::StructorIdentifier) {
if (QN->Components->Count < 2) {
Error = true;
return nullptr;
}
StructorIdentifierNode *SIN =
static_cast<StructorIdentifierNode *>(Identifier);
Node *ClassNode = QN->Components->Nodes[QN->Components->Count - 2];
SIN->Class = static_cast<IdentifierNode *>(ClassNode);
}
assert(QN);
return QN;
}
IdentifierNode *Demangler::demangleUnqualifiedTypeName(StringView &MangledName,
bool Memorize) {
// An inner-most name can be a back-reference, because a fully-qualified name
// (e.g. Scope + Inner) can contain other fully qualified names inside of
// them (for example template parameters), and these nested parameters can
// refer to previously mangled types.
if (startsWithDigit(MangledName))
return demangleBackRefName(MangledName);
if (MangledName.startsWith("?$"))
return demangleTemplateInstantiationName(MangledName, NBB_Template);
return demangleSimpleName(MangledName, Memorize);
}
IdentifierNode *
Demangler::demangleUnqualifiedSymbolName(StringView &MangledName,
NameBackrefBehavior NBB) {
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
if (startsWithDigit(MangledName))
return demangleBackRefName(MangledName);
if (MangledName.startsWith("?$"))
return demangleTemplateInstantiationName(MangledName, NBB);
if (MangledName.startsWith('?'))
return demangleFunctionIdentifierCode(MangledName);
return demangleSimpleName(MangledName, /*Memorize=*/(NBB & NBB_Simple) != 0);
}
IdentifierNode *Demangler::demangleNameScopePiece(StringView &MangledName) {
if (startsWithDigit(MangledName))
return demangleBackRefName(MangledName);
if (MangledName.startsWith("?$"))
return demangleTemplateInstantiationName(MangledName, NBB_Template);
if (MangledName.startsWith("?A"))
return demangleAnonymousNamespaceName(MangledName);
[MS Demangler] Demangle symbols in function scopes. There are a couple of issues you run into when you start getting into more complex names, especially with regards to function local statics. When you've got something like: int x() { static int n = 0; return n; } Then this needs to demangle to something like int `int __cdecl x()'::`1'::n The nested mangled symbols (e.g. `int __cdecl x()` in the above example) also share state with regards to back-referencing, so we need to be able to re-use the demangler in the middle of demangling a symbol while sharing back-ref state. To make matters more complicated, there are a lot of ambiguities when demangling a symbol's qualified name, because a function local scope pattern (usually something like `?1??name?`) looks suspiciously like many other possible things that can occur, such as `?1` meaning the second back-ref and disambiguating these cases is rather interesting. The `?1?` in a local scope pattern is actually a special case of the more general pattern of `? + <encoded number> + ?`, where "encoded number" can itself have embedded `@` symbols, which is a common delimeter in mangled names. So we have to take care during the disambiguation, which is the reason for the overly complicated `isLocalScopePattern` function in this patch. I've added some pretty obnoxious tests to exercise all of this, which exposed several other problems related to back-referencing, so those are fixed here as well. Finally, I've uncommented some tests that were previously marked as `FIXME`, since now these work. Differential Revision: https://reviews.llvm.org/D49965 llvm-svn: 338226
2018-07-30 11:12:34 +08:00
if (startsWithLocalScopePattern(MangledName))
return demangleLocallyScopedNamePiece(MangledName);
return demangleSimpleName(MangledName, /*Memorize=*/true);
}
static NodeArrayNode *nodeListToNodeArray(ArenaAllocator &Arena, NodeList *Head,
size_t Count) {
NodeArrayNode *N = Arena.alloc<NodeArrayNode>();
N->Count = Count;
N->Nodes = Arena.allocArray<Node *>(Count);
for (size_t I = 0; I < Count; ++I) {
N->Nodes[I] = Head->N;
Head = Head->Next;
}
return N;
}
QualifiedNameNode *
Demangler::demangleNameScopeChain(StringView &MangledName,
IdentifierNode *UnqualifiedName) {
NodeList *Head = Arena.alloc<NodeList>();
Head->N = UnqualifiedName;
size_t Count = 1;
while (!MangledName.consumeFront("@")) {
++Count;
NodeList *NewHead = Arena.alloc<NodeList>();
NewHead->Next = Head;
Head = NewHead;
if (MangledName.empty()) {
Error = true;
return nullptr;
}
assert(!Error);
IdentifierNode *Elem = demangleNameScopePiece(MangledName);
if (Error)
return nullptr;
Head->N = Elem;
}
QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();
QN->Components = nodeListToNodeArray(Arena, Head, Count);
return QN;
}
FuncClass Demangler::demangleFunctionClass(StringView &MangledName) {
switch (MangledName.popFront()) {
case '9':
return FuncClass(FC_ExternC | FC_NoParameterList);
case 'A':
return FC_Private;
case 'B':
return FuncClass(FC_Private | FC_Far);
case 'C':
return FuncClass(FC_Private | FC_Static);
case 'D':
return FuncClass(FC_Private | FC_Static | FC_Far);
case 'E':
return FuncClass(FC_Private | FC_Virtual);
case 'F':
return FuncClass(FC_Private | FC_Virtual | FC_Far);
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
case 'G':
return FuncClass(FC_Private | FC_StaticThisAdjust);
case 'H':
return FuncClass(FC_Private | FC_StaticThisAdjust | FC_Far);
case 'I':
return FuncClass(FC_Protected);
case 'J':
return FuncClass(FC_Protected | FC_Far);
case 'K':
return FuncClass(FC_Protected | FC_Static);
case 'L':
return FuncClass(FC_Protected | FC_Static | FC_Far);
case 'M':
return FuncClass(FC_Protected | FC_Virtual);
case 'N':
return FuncClass(FC_Protected | FC_Virtual | FC_Far);
case 'O':
return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust);
case 'P':
return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust | FC_Far);
case 'Q':
return FuncClass(FC_Public);
case 'R':
return FuncClass(FC_Public | FC_Far);
case 'S':
return FuncClass(FC_Public | FC_Static);
case 'T':
return FuncClass(FC_Public | FC_Static | FC_Far);
case 'U':
return FuncClass(FC_Public | FC_Virtual);
case 'V':
return FuncClass(FC_Public | FC_Virtual | FC_Far);
case 'W':
return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust);
case 'X':
return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust | FC_Far);
case 'Y':
return FuncClass(FC_Global);
case 'Z':
return FuncClass(FC_Global | FC_Far);
case '$': {
FuncClass VFlag = FC_VirtualThisAdjust;
if (MangledName.consumeFront('R'))
VFlag = FuncClass(VFlag | FC_VirtualThisAdjustEx);
if (MangledName.empty())
break;
switch (MangledName.popFront()) {
case '0':
return FuncClass(FC_Private | FC_Virtual | VFlag);
case '1':
return FuncClass(FC_Private | FC_Virtual | VFlag | FC_Far);
case '2':
return FuncClass(FC_Protected | FC_Virtual | VFlag);
case '3':
return FuncClass(FC_Protected | FC_Virtual | VFlag | FC_Far);
case '4':
return FuncClass(FC_Public | FC_Virtual | VFlag);
case '5':
return FuncClass(FC_Public | FC_Virtual | VFlag | FC_Far);
}
}
}
Error = true;
return FC_Public;
}
CallingConv Demangler::demangleCallingConvention(StringView &MangledName) {
if (MangledName.empty()) {
Error = true;
return CallingConv::None;
}
switch (MangledName.popFront()) {
case 'A':
case 'B':
return CallingConv::Cdecl;
case 'C':
case 'D':
return CallingConv::Pascal;
case 'E':
case 'F':
return CallingConv::Thiscall;
case 'G':
case 'H':
return CallingConv::Stdcall;
case 'I':
case 'J':
return CallingConv::Fastcall;
case 'M':
case 'N':
return CallingConv::Clrcall;
case 'O':
case 'P':
return CallingConv::Eabi;
case 'Q':
return CallingConv::Vectorcall;
case 'S':
return CallingConv::Swift;
}
return CallingConv::None;
}
StorageClass Demangler::demangleVariableStorageClass(StringView &MangledName) {
assert(MangledName.front() >= '0' && MangledName.front() <= '4');
switch (MangledName.popFront()) {
case '0':
return StorageClass::PrivateStatic;
case '1':
return StorageClass::ProtectedStatic;
case '2':
return StorageClass::PublicStatic;
case '3':
return StorageClass::Global;
case '4':
return StorageClass::FunctionLocalStatic;
}
DEMANGLE_UNREACHABLE;
}
std::pair<Qualifiers, bool>
Demangler::demangleQualifiers(StringView &MangledName) {
if (MangledName.empty()) {
Error = true;
return std::make_pair(Q_None, false);
}
switch (MangledName.popFront()) {
// Member qualifiers
case 'Q':
return std::make_pair(Q_None, true);
case 'R':
return std::make_pair(Q_Const, true);
case 'S':
return std::make_pair(Q_Volatile, true);
case 'T':
return std::make_pair(Qualifiers(Q_Const | Q_Volatile), true);
// Non-Member qualifiers
case 'A':
return std::make_pair(Q_None, false);
case 'B':
return std::make_pair(Q_Const, false);
case 'C':
return std::make_pair(Q_Volatile, false);
case 'D':
return std::make_pair(Qualifiers(Q_Const | Q_Volatile), false);
}
Error = true;
return std::make_pair(Q_None, false);
}
// <variable-type> ::= <type> <cvr-qualifiers>
// ::= <type> <pointee-cvr-qualifiers> # pointers, references
TypeNode *Demangler::demangleType(StringView &MangledName,
QualifierMangleMode QMM) {
Qualifiers Quals = Q_None;
bool IsMember = false;
if (QMM == QualifierMangleMode::Mangle) {
std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
} else if (QMM == QualifierMangleMode::Result) {
if (MangledName.consumeFront('?'))
std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
}
if (MangledName.empty()) {
Error = true;
return nullptr;
}
TypeNode *Ty = nullptr;
if (isTagType(MangledName))
Ty = demangleClassType(MangledName);
else if (isPointerType(MangledName)) {
if (isMemberPointer(MangledName, Error))
Ty = demangleMemberPointerType(MangledName);
else if (!Error)
Ty = demanglePointerType(MangledName);
else
return nullptr;
} else if (isArrayType(MangledName))
Ty = demangleArrayType(MangledName);
else if (isFunctionType(MangledName)) {
if (MangledName.consumeFront("$$A8@@"))
Ty = demangleFunctionType(MangledName, true);
else {
assert(MangledName.startsWith("$$A6"));
MangledName.consumeFront("$$A6");
Ty = demangleFunctionType(MangledName, false);
}
} else if (isCustomType(MangledName)) {
Ty = demangleCustomType(MangledName);
} else {
Ty = demanglePrimitiveType(MangledName);
}
if (!Ty || Error)
return Ty;
Ty->Quals = Qualifiers(Ty->Quals | Quals);
return Ty;
}
bool Demangler::demangleThrowSpecification(StringView &MangledName) {
if (MangledName.consumeFront("_E"))
return true;
if (MangledName.consumeFront('Z'))
return false;
Error = true;
return false;
}
FunctionSignatureNode *Demangler::demangleFunctionType(StringView &MangledName,
bool HasThisQuals) {
FunctionSignatureNode *FTy = Arena.alloc<FunctionSignatureNode>();
if (HasThisQuals) {
FTy->Quals = demanglePointerExtQualifiers(MangledName);
FTy->RefQualifier = demangleFunctionRefQualifier(MangledName);
FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first);
}
// Fields that appear on both member and non-member functions.
FTy->CallConvention = demangleCallingConvention(MangledName);
// <return-type> ::= <type>
// ::= @ # structors (they have no declared return type)
bool IsStructor = MangledName.consumeFront('@');
if (!IsStructor)
FTy->ReturnType = demangleType(MangledName, QualifierMangleMode::Result);
FTy->Params = demangleFunctionParameterList(MangledName, FTy->IsVariadic);
FTy->IsNoexcept = demangleThrowSpecification(MangledName);
return FTy;
}
FunctionSymbolNode *
Demangler::demangleFunctionEncoding(StringView &MangledName) {
FuncClass ExtraFlags = FC_None;
if (MangledName.consumeFront("$$J0"))
ExtraFlags = FC_ExternC;
if (MangledName.empty()) {
Error = true;
return nullptr;
}
FuncClass FC = demangleFunctionClass(MangledName);
FC = FuncClass(ExtraFlags | FC);
FunctionSignatureNode *FSN = nullptr;
ThunkSignatureNode *TTN = nullptr;
if (FC & FC_StaticThisAdjust) {
TTN = Arena.alloc<ThunkSignatureNode>();
TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);
} else if (FC & FC_VirtualThisAdjust) {
TTN = Arena.alloc<ThunkSignatureNode>();
if (FC & FC_VirtualThisAdjustEx) {
TTN->ThisAdjust.VBPtrOffset = demangleSigned(MangledName);
TTN->ThisAdjust.VBOffsetOffset = demangleSigned(MangledName);
}
TTN->ThisAdjust.VtordispOffset = demangleSigned(MangledName);
TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);
}
if (FC & FC_NoParameterList) {
// This is an extern "C" function whose full signature hasn't been mangled.
// This happens when we need to mangle a local symbol inside of an extern
// "C" function.
FSN = Arena.alloc<FunctionSignatureNode>();
} else {
bool HasThisQuals = !(FC & (FC_Global | FC_Static));
FSN = demangleFunctionType(MangledName, HasThisQuals);
}
if (Error)
return nullptr;
if (TTN) {
*static_cast<FunctionSignatureNode *>(TTN) = *FSN;
FSN = TTN;
}
FSN->FunctionClass = FC;
FunctionSymbolNode *Symbol = Arena.alloc<FunctionSymbolNode>();
Symbol->Signature = FSN;
return Symbol;
}
CustomTypeNode *Demangler::demangleCustomType(StringView &MangledName) {
assert(MangledName.startsWith('?'));
MangledName.popFront();
CustomTypeNode *CTN = Arena.alloc<CustomTypeNode>();
CTN->Identifier = demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true);
if (!MangledName.consumeFront('@'))
Error = true;
if (Error)
return nullptr;
return CTN;
}
// Reads a primitive type.
PrimitiveTypeNode *Demangler::demanglePrimitiveType(StringView &MangledName) {
if (MangledName.consumeFront("$$T"))
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Nullptr);
switch (MangledName.popFront()) {
case 'X':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Void);
case 'D':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char);
case 'C':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Schar);
case 'E':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uchar);
case 'F':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Short);
case 'G':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ushort);
case 'H':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int);
case 'I':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint);
case 'J':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Long);
case 'K':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ulong);
case 'M':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Float);
case 'N':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Double);
case 'O':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ldouble);
case '_': {
if (MangledName.empty()) {
Error = true;
return nullptr;
}
switch (MangledName.popFront()) {
case 'N':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Bool);
case 'J':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int64);
case 'K':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint64);
case 'W':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Wchar);
case 'Q':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char8);
case 'S':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char16);
case 'U':
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char32);
}
break;
}
}
Error = true;
return nullptr;
}
TagTypeNode *Demangler::demangleClassType(StringView &MangledName) {
TagTypeNode *TT = nullptr;
switch (MangledName.popFront()) {
case 'T':
TT = Arena.alloc<TagTypeNode>(TagKind::Union);
break;
case 'U':
TT = Arena.alloc<TagTypeNode>(TagKind::Struct);
break;
case 'V':
TT = Arena.alloc<TagTypeNode>(TagKind::Class);
break;
case 'W':
if (!MangledName.consumeFront('4')) {
Error = true;
return nullptr;
}
TT = Arena.alloc<TagTypeNode>(TagKind::Enum);
break;
default:
assert(false);
}
TT->QualifiedName = demangleFullyQualifiedTypeName(MangledName);
return TT;
}
// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type>
// # the E is required for 64-bit non-static pointers
PointerTypeNode *Demangler::demanglePointerType(StringView &MangledName) {
PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();
std::tie(Pointer->Quals, Pointer->Affinity) =
demanglePointerCVQualifiers(MangledName);
if (MangledName.consumeFront("6")) {
Pointer->Pointee = demangleFunctionType(MangledName, false);
return Pointer;
}
Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle);
return Pointer;
}
PointerTypeNode *Demangler::demangleMemberPointerType(StringView &MangledName) {
PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();
std::tie(Pointer->Quals, Pointer->Affinity) =
demanglePointerCVQualifiers(MangledName);
assert(Pointer->Affinity == PointerAffinity::Pointer);
Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
// isMemberPointer() only returns true if there is at least one character
// after the qualifiers.
if (MangledName.consumeFront("8")) {
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);
Pointer->Pointee = demangleFunctionType(MangledName, true);
} else {
Qualifiers PointeeQuals = Q_None;
bool IsMember = false;
std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName);
assert(IsMember || Error);
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);
Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop);
if (Pointer->Pointee)
Pointer->Pointee->Quals = PointeeQuals;
}
return Pointer;
}
Qualifiers Demangler::demanglePointerExtQualifiers(StringView &MangledName) {
Qualifiers Quals = Q_None;
if (MangledName.consumeFront('E'))
Quals = Qualifiers(Quals | Q_Pointer64);
if (MangledName.consumeFront('I'))
Quals = Qualifiers(Quals | Q_Restrict);
if (MangledName.consumeFront('F'))
Quals = Qualifiers(Quals | Q_Unaligned);
return Quals;
}
ArrayTypeNode *Demangler::demangleArrayType(StringView &MangledName) {
assert(MangledName.front() == 'Y');
MangledName.popFront();
uint64_t Rank = 0;
bool IsNegative = false;
std::tie(Rank, IsNegative) = demangleNumber(MangledName);
if (IsNegative || Rank == 0) {
Error = true;
return nullptr;
}
ArrayTypeNode *ATy = Arena.alloc<ArrayTypeNode>();
NodeList *Head = Arena.alloc<NodeList>();
NodeList *Tail = Head;
for (uint64_t I = 0; I < Rank; ++I) {
uint64_t D = 0;
std::tie(D, IsNegative) = demangleNumber(MangledName);
if (Error || IsNegative) {
Error = true;
return nullptr;
}
Tail->N = Arena.alloc<IntegerLiteralNode>(D, IsNegative);
if (I + 1 < Rank) {
Tail->Next = Arena.alloc<NodeList>();
Tail = Tail->Next;
}
}
ATy->Dimensions = nodeListToNodeArray(Arena, Head, Rank);
if (MangledName.consumeFront("$$C")) {
bool IsMember = false;
std::tie(ATy->Quals, IsMember) = demangleQualifiers(MangledName);
if (IsMember) {
Error = true;
return nullptr;
}
}
ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop);
return ATy;
}
// Reads a function's parameters.
NodeArrayNode *Demangler::demangleFunctionParameterList(StringView &MangledName,
bool &IsVariadic) {
// Empty parameter list.
if (MangledName.consumeFront('X'))
return nullptr;
NodeList *Head = Arena.alloc<NodeList>();
NodeList **Current = &Head;
size_t Count = 0;
while (!Error && !MangledName.startsWith('@') &&
!MangledName.startsWith('Z')) {
++Count;
if (startsWithDigit(MangledName)) {
size_t N = MangledName[0] - '0';
if (N >= Backrefs.FunctionParamCount) {
Error = true;
return nullptr;
}
MangledName = MangledName.dropFront();
*Current = Arena.alloc<NodeList>();
(*Current)->N = Backrefs.FunctionParams[N];
Current = &(*Current)->Next;
continue;
}
size_t OldSize = MangledName.size();
*Current = Arena.alloc<NodeList>();
TypeNode *TN = demangleType(MangledName, QualifierMangleMode::Drop);
if (!TN || Error)
return nullptr;
(*Current)->N = TN;
size_t CharsConsumed = OldSize - MangledName.size();
assert(CharsConsumed != 0);
// Single-letter types are ignored for backreferences because memorizing
// them doesn't save anything.
if (Backrefs.FunctionParamCount <= 9 && CharsConsumed > 1)
Backrefs.FunctionParams[Backrefs.FunctionParamCount++] = TN;
Current = &(*Current)->Next;
}
if (Error)
return nullptr;
NodeArrayNode *NA = nodeListToNodeArray(Arena, Head, Count);
// A non-empty parameter list is terminated by either 'Z' (variadic) parameter
// list or '@' (non variadic). Careful not to consume "@Z", as in that case
// the following Z could be a throw specifier.
if (MangledName.consumeFront('@'))
return NA;
if (MangledName.consumeFront('Z')) {
IsVariadic = true;
return NA;
}
DEMANGLE_UNREACHABLE;
}
NodeArrayNode *
Demangler::demangleTemplateParameterList(StringView &MangledName) {
NodeList *Head = nullptr;
NodeList **Current = &Head;
size_t Count = 0;
while (!MangledName.startsWith('@')) {
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") ||
MangledName.consumeFront("$$$V") || MangledName.consumeFront("$$Z")) {
// parameter pack separator
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
continue;
}
++Count;
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
// Template parameter lists don't participate in back-referencing.
*Current = Arena.alloc<NodeList>();
NodeList &TP = **Current;
TemplateParameterReferenceNode *TPRN = nullptr;
[MS Demangler] Fix several crashes and demangling bugs. These bugs were found by writing a Python script which spidered the entire Chromium build directory tree demangling every symbol in every object file. At the start, the tool printed: Processed 27443 object files. 2926377/2936108 symbols successfully demangled (99.6686%) 9731 symbols could not be demangled (0.3314%) 14589 files crashed while demangling (53.1611%) After this patch, it prints: Processed 27443 object files. 41295518/41295617 symbols successfully demangled (99.9998%) 99 symbols could not be demangled (0.0002%) 0 files crashed while demangling (0.0000%) The issues fixed in this patch are: * Ignore empty parameter packs. Previously we would encounter a mangling for an empty parameter pack and add a null node to the AST. Since we don't print these anyway, we now just don't add anything to the AST and ignore it entirely. This fixes some of the crashes. * Account for "incorrect" string literal demanglings. Apparently an older version of clang would not truncate mangled string literals to 32 bytes of encoded character data. The demangling code however would allocate a 32 byte buffer thinking that it would not encounter more than this, and overrun the buffer. We now demangle up to 128 bytes of data, since the buggy clang would encode up to 32 *characters* of data. * Extended support for demangling init-fini stubs. If you had something like struct Foo { static vector<string> S; }; this would generate a dynamic atexit initializer *for the variable*. We didn't handle this, but now we print something nice. This is actually an improvement over undname, which will fail to demangle this at all. * Fixed one case of static this adjustment. We weren't handling several thunk codes so we didn't recognize the mangling. These are now handled. * Fixed a back-referencing problem. Member pointer templates should have their components considered for back-referencing The remaining 99 symbols which can't be demangled are all symbols which are compiler-generated and undname can't demangle either. llvm-svn: 341000
2018-08-30 07:56:09 +08:00
if (MangledName.consumeFront("$$Y")) {
// Template alias
TP.N = demangleFullyQualifiedTypeName(MangledName);
} else if (MangledName.consumeFront("$$B")) {
// Array
TP.N = demangleType(MangledName, QualifierMangleMode::Drop);
} else if (MangledName.consumeFront("$$C")) {
// Type has qualifiers.
TP.N = demangleType(MangledName, QualifierMangleMode::Mangle);
} else if (MangledName.startsWith("$1") || MangledName.startsWith("$H") ||
MangledName.startsWith("$I") || MangledName.startsWith("$J")) {
// Pointer to member
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
TPRN->IsMemberPointer = true;
MangledName = MangledName.dropFront();
// 1 - single inheritance <name>
// H - multiple inheritance <name> <number>
// I - virtual inheritance <name> <number> <number>
// J - unspecified inheritance <name> <number> <number> <number>
char InheritanceSpecifier = MangledName.popFront();
SymbolNode *S = nullptr;
if (MangledName.startsWith('?')) {
S = parse(MangledName);
if (Error || !S->Name) {
Error = true;
return nullptr;
}
memorizeIdentifier(S->Name->getUnqualifiedIdentifier());
}
switch (InheritanceSpecifier) {
case 'J':
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
demangleSigned(MangledName);
DEMANGLE_FALLTHROUGH;
case 'I':
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
demangleSigned(MangledName);
DEMANGLE_FALLTHROUGH;
case 'H':
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
demangleSigned(MangledName);
DEMANGLE_FALLTHROUGH;
case '1':
break;
default:
DEMANGLE_UNREACHABLE;
}
TPRN->Affinity = PointerAffinity::Pointer;
TPRN->Symbol = S;
} else if (MangledName.startsWith("$E?")) {
MangledName.consumeFront("$E");
// Reference to symbol
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
TPRN->Symbol = parse(MangledName);
TPRN->Affinity = PointerAffinity::Reference;
} else if (MangledName.startsWith("$F") || MangledName.startsWith("$G")) {
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
// Data member pointer.
MangledName = MangledName.dropFront();
char InheritanceSpecifier = MangledName.popFront();
switch (InheritanceSpecifier) {
case 'G':
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
demangleSigned(MangledName);
DEMANGLE_FALLTHROUGH;
case 'F':
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
demangleSigned(MangledName);
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
demangleSigned(MangledName);
break;
default:
DEMANGLE_UNREACHABLE;
}
TPRN->IsMemberPointer = true;
} else if (MangledName.consumeFront("$0")) {
// Integral non-type template parameter
bool IsNegative = false;
uint64_t Value = 0;
std::tie(Value, IsNegative) = demangleNumber(MangledName);
TP.N = Arena.alloc<IntegerLiteralNode>(Value, IsNegative);
} else {
TP.N = demangleType(MangledName, QualifierMangleMode::Drop);
}
if (Error)
return nullptr;
Current = &TP.Next;
}
// The loop above returns nullptr on Error.
assert(!Error);
// Template parameter lists cannot be variadic, so it can only be terminated
// by @ (as opposed to 'Z' in the function parameter case).
assert(MangledName.startsWith('@')); // The above loop exits only on '@'.
MangledName.consumeFront('@');
return nodeListToNodeArray(Arena, Head, Count);
}
void Demangler::dumpBackReferences() {
std::printf("%d function parameter backreferences\n",
(int)Backrefs.FunctionParamCount);
// Create an output stream so we can render each type.
OutputStream OS;
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
std::terminate();
for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) {
OS.setCurrentPosition(0);
TypeNode *T = Backrefs.FunctionParams[I];
T->output(OS, OF_Default);
std::printf(" [%d] - %.*s\n", (int)I, (int)OS.getCurrentPosition(),
OS.getBuffer());
}
std::free(OS.getBuffer());
if (Backrefs.FunctionParamCount > 0)
std::printf("\n");
std::printf("%d name backreferences\n", (int)Backrefs.NamesCount);
for (size_t I = 0; I < Backrefs.NamesCount; ++I) {
std::printf(" [%d] - %.*s\n", (int)I, (int)Backrefs.Names[I]->Name.size(),
Backrefs.Names[I]->Name.begin());
}
if (Backrefs.NamesCount > 0)
std::printf("\n");
}
char *llvm::microsoftDemangle(const char *MangledName, size_t *NMangled,
char *Buf, size_t *N,
int *Status, MSDemangleFlags Flags) {
Demangler D;
OutputStream S;
StringView Name{MangledName};
SymbolNode *AST = D.parse(Name);
if (!D.Error && NMangled)
*NMangled = Name.begin() - MangledName;
if (Flags & MSDF_DumpBackrefs)
D.dumpBackReferences();
OutputFlags OF = OF_Default;
if (Flags & MSDF_NoCallingConvention)
OF = OutputFlags(OF | OF_NoCallingConvention);
if (Flags & MSDF_NoAccessSpecifier)
OF = OutputFlags(OF | OF_NoAccessSpecifier);
if (Flags & MSDF_NoReturnType)
OF = OutputFlags(OF | OF_NoReturnType);
if (Flags & MSDF_NoMemberType)
OF = OutputFlags(OF | OF_NoMemberType);
int InternalStatus = demangle_success;
if (D.Error)
InternalStatus = demangle_invalid_mangled_name;
else if (!initializeOutputStream(Buf, N, S, 1024))
InternalStatus = demangle_memory_alloc_failure;
else {
AST->output(S, OF);
S += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
Buf = S.getBuffer();
}
if (Status)
*Status = InternalStatus;
return InternalStatus == demangle_success ? Buf : nullptr;
}