diff options
| author | Peter LaFosse <peter@vector35.com> | 2026-03-10 15:35:26 -0400 |
|---|---|---|
| committer | Peter LaFosse <peter@vector35.com> | 2026-03-26 17:56:23 -0400 |
| commit | af93db2cbfd24d5da40ed2951f5b9b05bf3d565c (patch) | |
| tree | a2e21d0d21fe336e3ae2d0b6297076952ee6dafa /demangler | |
| parent | 9d6b64e8697ec85e8111b3d847b3e6d5853cf599 (diff) | |
GNU3 Demangler: fix substitution tracking, expressions, and type names
Substitution table and scoping:
- Fix generic lambda auto parameters: save/replace m_templateSubstitute
with 'auto' placeholders for lambda-local T_/T0_/T1_ resolution
- Fix local-name template scoping with m_inLocalName flag
- Fix substitution table off-by-one for all-type template args
- Fix pointer-to-member type (case 'M'): set substitute=true per ABI
- Fix multi-character seq-id parsing (S10_, S11_, etc.)
- Fix DemangleUnresolvedType decltype (DT/Dt) prefix consumption
- Add forward template ref support for cv conversion operators
- Fix sr qualifier-list PushType and conditional E handling
- Fix multi-level sr scope resolution without N prefix
- Fix sr + unscoped template substitution table tracking
- Fix I...E argument pack handling (GCC I...E vs J...E)
- Fix Dp pack expansion to show '...' for concrete types
Expression formatting:
- Fix L_Z embedded mangled name template scope isolation
- Handle LZ<encoding>E function address template args
- cl expression: callable(args) format with function substitute tracking
- dt/pt expressions: obj.member and obj->member directly
- fp_/fp<N>_ placeholders in decltype return types
- Fix fL function-param scope validation
- Fix DemangleBaseUnresolvedName 'on' prefix consumption
Type names (Itanium ABI spec compliance):
- x/y: "long long"/"unsigned long long"
- a: "signed char" (distinct from 'c' = "char")
- n/o: "__int128"/"unsigned __int128"
- g: "__float128" via FloatType altName
- Dd/Df/De: "decimal64"/"decimal32"/"decimal128"
- Dh: "_Float16"
- Dp: always show "..." for pack expansion
- Fix 'n' literal: __int128 not __uint128
- Fix dot extension spacing
Cleanup:
- Remove unused NextIsOneOf, ReadUntil, DemangleInitializer
- Guard GetRaw/GetTemplateType/PrintTables behind GNUDEMANGLE_DEBUG
- Remove unreachable GetNameType C/D cases
- Remove dead BinaryExpression "." check
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'demangler')
| -rw-r--r-- | demangler/gnu3/demangle_gnu3.cpp | 440 | ||||
| -rw-r--r-- | demangler/gnu3/demangle_gnu3.h | 29 | ||||
| -rw-r--r-- | demangler/gnu3/demangled_type_node.cpp | 11 | ||||
| -rw-r--r-- | demangler/gnu3/demangled_type_node.h | 2 |
4 files changed, 365 insertions, 117 deletions
diff --git a/demangler/gnu3/demangle_gnu3.cpp b/demangler/gnu3/demangle_gnu3.cpp index 8889c60b..5e598b4f 100644 --- a/demangler/gnu3/demangle_gnu3.cpp +++ b/demangler/gnu3/demangle_gnu3.cpp @@ -30,7 +30,7 @@ using namespace std; #endif -#define MAX_DEMANGLE_LENGTH 4096 +#define MAX_DEMANGLE_LENGTH 262144 #define hash(x,y) (64 * x + y) #undef GNUDEMANGLE_DEBUG @@ -39,11 +39,11 @@ static string _indent = ""; #define indent() _indent += " "; #define dedent() do {if (_indent.size() > 0) _indent = _indent.substr(1);}while(0); -void MyLogDebug(string fmt, ...) +void MyLogDebug(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(0, DebugLog, (_indent + fmt).c_str(), args); + PerformLog(0, DebugLog, "", 0, (_indent + fmt).c_str(), args); va_end(args); } #else @@ -218,17 +218,9 @@ static BNNameType GetNameType(char elm1, char elm2) case hash('d','a'): return OperatorDeleteArrayNameType; // delete[] case hash('n','w'): return OperatorNewNameType; // new case hash('n','a'): return OperatorNewArrayNameType; // new [] - case hash('C','1'): return ConstructorNameType; - case hash('C','2'): return ConstructorNameType; - case hash('C','3'): return ConstructorNameType; - case hash('C','4'): return ConstructorNameType; - case hash('C','5'): return ConstructorNameType; - case hash('D','0'): return DestructorNameType; - case hash('D','1'): return DestructorNameType; - case hash('D','2'): return DestructorNameType; - case hash('D','3'): return DestructorNameType; - case hash('D','4'): return DestructorNameType; - case hash('D','5'): return DestructorNameType; + // Note: C1-C5 (constructor) and D0-D5 (destructor) are handled directly + // by DemangleUnqualifiedName with their own case blocks, so they never + // reach GetNameType. default: return NoNameType; } @@ -270,22 +262,12 @@ string DemangleGNU3Reader::PeekString(size_t count) -bool DemangleGNU3Reader::NextIsOneOf(const string& list) -{ - char elm = Peek(); - for (auto a : list) - { - if (a == elm) - return true; - } - return false; -} - - +#ifdef GNUDEMANGLE_DEBUG string DemangleGNU3Reader::GetRaw() { return m_data.substr(m_offset); } +#endif @@ -300,14 +282,6 @@ string DemangleGNU3Reader::ReadString(size_t count) } -string DemangleGNU3Reader::ReadUntil(char sentinal) -{ - size_t pos = m_data.find_first_of(sentinal, m_offset); - if (pos == string::npos) - throw DemangleException(); - return ReadString(pos); -} - // ===== DemangleGNU3 implementation ===== @@ -318,7 +292,8 @@ DemangleGNU3::DemangleGNU3(Architecture* arch, const string& mangledName) : m_isParameter(false), m_shouldDeleteReader(true), m_topLevel(true), - m_isOperatorOverload(false) + m_isOperatorOverload(false), + m_permitForwardTemplateRefs(false) { MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); } @@ -340,6 +315,9 @@ void DemangleGNU3::Reset(Architecture* arch, const string& mangledName) m_shouldDeleteReader = true; m_topLevel = true; m_isOperatorOverload = false; + m_permitForwardTemplateRefs = false; + m_pendingForwardRefs.clear(); + m_inLocalName = false; } @@ -376,15 +354,14 @@ void DemangleGNU3::PushTemplateType(const DemangledTypeNode& type) } +#ifdef GNUDEMANGLE_DEBUG const DemangledTypeNode& DemangleGNU3::GetTemplateType(size_t ref) { if (ref >= m_templateSubstitute.size()) - { - // PrintTables(); throw DemangleException(); - } return m_templateSubstitute[ref]; } +#endif void DemangleGNU3::PushType(const DemangledTypeNode& type) @@ -396,14 +373,12 @@ void DemangleGNU3::PushType(const DemangledTypeNode& type) const DemangledTypeNode& DemangleGNU3::GetType(size_t ref) { if (ref >= m_substitute.size()) - { - // PrintTables(); throw DemangleException(); - } return m_substitute[ref]; } +#ifdef GNUDEMANGLE_DEBUG void DemangleGNU3::PrintTables() { LogDebug("Substitution Table\n"); @@ -418,6 +393,7 @@ void DemangleGNU3::PrintTables() LogDebug("[%d] %s\n", i-1, GetTemplateType(i).GetString().c_str()); } } +#endif void DemangleGNU3::DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct) @@ -492,7 +468,43 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl) } -const DemangledTypeNode& DemangleGNU3::DemangleTemplateSubstitution() +string DemangleGNU3::ForwardRefPlaceholder(size_t index) +{ + return "\x01FWDREF:" + to_string(index) + "\x01"; +} + + +void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode& type, const vector<string>& args) +{ + if (m_pendingForwardRefs.empty()) + return; + auto& segs = type.GetMutableTypeName(); + bool resolved = false; + for (const auto& fr : m_pendingForwardRefs) + { + string placeholder = ForwardRefPlaceholder(fr.index); + string replacement = (fr.index < args.size()) ? args[fr.index] : "auto"; + for (auto& seg : segs) + { + size_t pos; + while ((pos = seg.find(placeholder)) != string::npos) + { + seg.replace(pos, placeholder.size(), replacement); + resolved = true; + } + } + } + // Only clear the pending list when we actually resolved something. Inner + // nested-name 'I' handlers (e.g. template args of types nested inside the + // cv-operator result type) may call here with a type that does not contain + // the placeholder; we must not discard the pending entry in that case so + // that the correct outer 'I' handler can still resolve it. + if (resolved) + m_pendingForwardRefs.clear(); +} + + +DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution() { indent(); MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str()); @@ -524,7 +536,20 @@ const DemangledTypeNode& DemangleGNU3::DemangleTemplateSubstitution() throw DemangleException(); } dedent(); - return GetTemplateType(number); + + if (number < m_templateSubstitute.size()) + return m_templateSubstitute[number]; + + // If forward template references are permitted (e.g. inside a cv conversion + // operator type), return a placeholder that will be resolved once the outer + // template args are known. + if (m_permitForwardTemplateRefs) + { + m_pendingForwardRefs.push_back({number}); + return CreateUnknownType(ForwardRefPlaceholder(number)); + } + + throw DemangleException(); } @@ -627,8 +652,11 @@ DemangledTypeNode DemangleGNU3::DemangleType() //Template Substitution type = DemangleTemplateSubstitution(); - substitute = true; - if (m_reader.Peek() == 'I') + // In forward-ref mode (cv conversion operator type parsing), do not consume + // trailing I<args>E — it belongs to the enclosing nested-name and will be + // processed by DemangleNestedName's 'I' case, which resolves forward refs. + substitute = !m_permitForwardTemplateRefs; + if (!m_permitForwardTemplateRefs && m_reader.Peek() == 'I') { m_reader.Consume(); if (substitute) @@ -703,8 +731,8 @@ DemangledTypeNode DemangleGNU3::DemangleType() case 'v': type = DemangledTypeNode::VoidType(); break; case 'w': type = DemangledTypeNode::IntegerType(4, false, "wchar_t"); break; //TODO: verify case 'b': type = DemangledTypeNode::BoolType(); break; - case 'c': type = DemangledTypeNode::IntegerType(1, true); break; - case 'a': type = DemangledTypeNode::IntegerType(1, true); break; + case 'c': type = DemangledTypeNode::IntegerType(1, true, "char"); break; + case 'a': type = DemangledTypeNode::IntegerType(1, true, "signed char"); break; case 'h': type = DemangledTypeNode::IntegerType(1, false); break; case 's': type = DemangledTypeNode::IntegerType(2, true); break; case 't': type = DemangledTypeNode::IntegerType(2, false); break; @@ -730,15 +758,16 @@ DemangledTypeNode DemangleGNU3::DemangleType() //DemangledTypeNode ptr = DemangledTypeNode::PointerType(m_arch, member, cnst, vltl); //QualifiedName qn({memberName.GetString(), "*"}); type = CreateUnknownType(fullName); + substitute = true; break; } case 'F': type = DemangleFunction(cnst, vltl); break; case 'D': switch (m_reader.Read()) { - case 'd': type = DemangledTypeNode::FloatType(8); break; - case 'e': type = DemangledTypeNode::FloatType(16); break; - case 'f': type = DemangledTypeNode::FloatType(4); break; + case 'd': type = DemangledTypeNode::FloatType(8, "decimal64"); break; + case 'e': type = DemangledTypeNode::FloatType(16, "decimal128"); break; + case 'f': type = DemangledTypeNode::FloatType(4, "decimal32"); break; case 'h': type = DemangledTypeNode::FloatType(2); break; case 'i': type = DemangledTypeNode::IntegerType(4, true, "char32_t"); break; case 's': type = DemangledTypeNode::IntegerType(2, true, "char16_t"); break; @@ -750,7 +779,12 @@ DemangledTypeNode DemangleGNU3::DemangleType() type = CreateUnknownType(stdNullptrTName); break; } - case 'p': type = DemangleType(); break; + case 'p': + { + DemangledTypeNode inner = DemangleType(); + type = CreateUnknownType(inner.GetString() + "..."); + break; + } case 't': case 'T': type = CreateUnknownType("decltype(" + DemangleExpression() + ")"); @@ -870,13 +904,24 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution() m_reader.UnRead(1); number = 0; } - else if (isdigit(elm)) - { - number = elm - '0' + 1; - } - else if (isupper(elm)) + else if (isdigit(elm) || isupper(elm)) { - number = elm - 'A' + 11; + // Seq-id is encoded in base 36 using 0-9 A-Z. + // The actual substitution index = base36_value + 1. + // This handles both single-char (S0_ ... SZ_) and + // multi-char (S10_, S11_, ...) seq-ids. + size_t base36 = isdigit(elm) ? (size_t)(elm - '0') : (size_t)(elm - 'A' + 10); + while (m_reader.Peek() != '_') + { + char c = m_reader.Read(); + if (isdigit(c)) + base36 = base36 * 36 + (size_t)(c - '0'); + else if (isupper(c)) + base36 = base36 * 36 + (size_t)(c - 'A' + 10); + else + throw DemangleException(); + } + number = base36 + 1; } else { @@ -890,7 +935,11 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution() } dedent(); - return GetType(number); + const DemangledTypeNode& resolved = GetType(number); + const auto& segs = resolved.GetTypeName(); + if (!segs.empty()) + m_lastName = segs.back(); + return resolved; } m_lastName = name.back(); dedent(); @@ -938,20 +987,6 @@ int64_t DemangleGNU3::DemangleNumber() } -string DemangleGNU3::DemangleInitializer() -{ - string out; - if (m_reader.ReadString(2) != "pi") - throw DemangleException(); - out += "("; - while (m_reader.Peek() != 'E') - out += DemangleExpression(); - m_reader.Consume(); - out += ")"; - return out; -} - - string DemangleGNU3::DemanglePrimaryExpression() { indent(); @@ -966,16 +1001,36 @@ string DemangleGNU3::DemanglePrimaryExpression() if (m_reader.PeekString(2) == "_Z") { m_reader.Consume(2); + // The embedded _Z... is an independent mangled name with its own + // template scope. Save and clear the template substitution table + // so inner T_ / T0_ etc. resolve within this symbol, not the outer + // one. Set m_topLevel = true so template args get pushed properly. + auto savedTemplateSubstitute = m_templateSubstitute; + m_templateSubstitute.clear(); oldTopLevel = m_topLevel; - m_topLevel = false; + m_topLevel = true; DemangledTypeNode t = DemangleSymbol(tmpList); m_topLevel = oldTopLevel; - out += t.GetStringBeforeName(); - out += tmpList.GetString(); - out += t.GetStringAfterName(); + m_templateSubstitute = std::move(savedTemplateSubstitute); + out += t.GetTypeAndName(tmpList); dedent() return out; } + // LZ<encoding>E: function address template arg (GCC/Clang, without leading underscore) + if (m_reader.Peek() == 'Z') + { + m_reader.Consume(); // 'Z' + auto savedTemplateSubstitute2 = m_templateSubstitute; + m_templateSubstitute.clear(); + oldTopLevel = m_topLevel; + m_topLevel = true; + DemangledTypeNode t2 = DemangleSymbol(tmpList); + m_topLevel = oldTopLevel; + m_templateSubstitute = std::move(savedTemplateSubstitute2); + out += t2.GetTypeAndName(tmpList); + dedent(); + return out; + } switch (m_reader.Read()) { case 'b': @@ -1026,7 +1081,7 @@ string DemangleGNU3::DemanglePrimaryExpression() case 'l': out = DemangleNumberAsString() + "l"; break; //long case 'x': out = DemangleNumberAsString() + "ll"; break; //long long case 's': out = "(short)" + DemangleNumberAsString(); break; //short - case 'n': out = "(__uint128)" + DemangleNumberAsString() + "ull"; break; //__int128 + case 'n': out = "(__int128)" + DemangleNumberAsString(); break; //__int128 case 'i': out = DemangleNumberAsString(); break; // int case 'm': out = DemangleNumberAsString() + "ul"; break; //unsigned long case 't': out = "(unsigned short)" + DemangleNumberAsString(); break; //unsigned short @@ -1065,8 +1120,6 @@ string DemangleGNU3::DemangleBinaryExpression(const string& op) const string lhs = "(" + DemangleExpression() + ")"; const string rhs = "(" + DemangleExpression() + ")"; dedent(); - if (op == ".") - return lhs + op + rhs; return lhs + " " + op + " " + rhs; } @@ -1233,6 +1286,13 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() string name; name = "'lambda"; vector<DemangledTypeNode> lambdaParams; + // Generic lambdas encode 'auto' params as T_, T0_, T1_... which reference + // the lambda's own operator() template params, not any outer template scope. + // Save and replace the template substitution table with 'auto' placeholders. + auto savedTemplateSubstitute = m_templateSubstitute; + m_templateSubstitute.clear(); + for (int autoIdx = 0; autoIdx < 16; autoIdx++) + m_templateSubstitute.push_back(CreateUnknownType("auto")); do { DemangledTypeNode param = DemangleType(); @@ -1241,6 +1301,7 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() lambdaParams.push_back(std::move(param)); }while (m_reader.Peek() != 'E'); m_reader.Consume(); + m_templateSubstitute = std::move(savedTemplateSubstitute); if (isdigit(m_reader.Peek())) { @@ -1280,8 +1341,22 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName() break; } case hash('c','v'): //type (expression) - outType = CreateUnknownType("operator " + DemangleType().GetString()); + { + // The conversion operator type may reference template params (T_, T0_, ...) + // that aren't yet in m_templateSubstitute (they're defined by a following + // I<args>E in the enclosing nested name). Set m_permitForwardTemplateRefs so + // that DemangleTemplateSubstitution() returns a placeholder instead of + // throwing, and don't consume trailing I<args>E in the T case of DemangleType. + // The outer DemangleNestedName case 'I' will parse those args and call + // ResolveForwardTemplateRefs() to patch the placeholders. + bool savedPermit = m_permitForwardTemplateRefs; + m_pendingForwardRefs.clear(); + m_permitForwardTemplateRefs = true; + DemangledTypeNode cvType = DemangleType(); + m_permitForwardTemplateRefs = savedPermit; + outType = CreateUnknownType("operator " + cvType.GetString()); break; + } default: m_reader.UnRead(2); if (isdigit(m_reader.Peek()) || m_reader.Read() == 'L') @@ -1330,6 +1405,7 @@ QualifiedName DemangleGNU3::DemangleBaseUnresolvedName() const string str = m_reader.PeekString(2); if (str == "on") { + m_reader.Consume(); m_reader.Consume(); // skip 'o','n' prefix out.push_back(GetOperator(m_reader.Read(), m_reader.Read())); if (m_reader.Peek() == 'I') { @@ -1388,10 +1464,21 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType() type.SetHasTemplateArguments(true); PushType(type); } + else + { + // Template param used as scope qualifier (e.g. sr T_ name) is a substitution + // candidate: the compiler adds it to the main sub table so subsequent + // occurrences can use Sn_ instead of T_. + PushType(type); + } } else if (m_reader.Length() > 2 && (m_reader.PeekString(2) == "Dt" || m_reader.PeekString(2) == "DT")) { + m_reader.Consume(); // 'D' + m_reader.Consume(); // 't' or 'T' const string name = "decltype(" + DemangleExpression() + ")"; + if (m_reader.Read() != 'E') + throw DemangleException(); type = CreateUnknownType(name); } else if (m_reader.Peek() == 'S') @@ -1525,13 +1612,23 @@ string DemangleGNU3::DemangleExpression() case hash('p','p'): // ++ (postfix in <expression> context) case hash('m','m'): // -- (postfix in <expression> context) return DemangleUnarySuffixExpression(GetOperator(elm1, elm2)); + case hash('d','t'): // . + { + const string dtObj = DemangleExpression(); + const string dtMem = DemangleExpression(); + return dtObj + "." + dtMem; + } + case hash('p','t'): // -> + { + const string ptObj = DemangleExpression(); + const string ptMem = DemangleExpression(); + return ptObj + "->" + ptMem; + } case hash('l','s'): // << case hash('r','s'): // >> case hash('a','S'): // = case hash('e','q'): // == case hash('n','e'): // != - case hash('d','t'): // . - case hash('p','t'): // -> case hash('m','l'): // * case hash('m','i'): // - case hash('p','l'): // + @@ -1570,7 +1667,23 @@ string DemangleGNU3::DemangleExpression() DemangleExpression() + ":" + DemangleExpression(); case hash('c','l'): // () - return "(" + DemangleExpressionList() + ")"; + { + const string callable = DemangleExpression(); + string args; + bool firstArg = true; + m_functionSubstitute.push_back({}); + while (m_reader.Peek() != 'E') + { + if (!firstArg) args += ", "; + const string e = DemangleExpression(); + args += e; + m_functionSubstitute.back().push_back(CreateUnknownType(e)); + firstArg = false; + } + m_functionSubstitute.pop_back(); + m_reader.Consume(); // 'E' + return callable + "(" + args + ")"; + } case hash('c','v'): //type (expression) { DemangledTypeNode type = DemangleType(); @@ -1604,9 +1717,6 @@ string DemangleGNU3::DemangleExpression() int64_t listNumber = 0; int64_t elementNum = 0; char elm; - if (m_functionSubstitute.size() == 0) - throw DemangleException(); - if (elm2 == 'L') { // fL <L-1 num> p <CV> [<prm-2 num>] _ @@ -1622,20 +1732,27 @@ string DemangleGNU3::DemangleExpression() if (elm == '_') { m_reader.Consume(1); - if ((size_t)elementNum >= m_functionSubstitute[listNumber].size()) + if ((uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || + (size_t)elementNum >= m_functionSubstitute[listNumber].size()) { - throw DemangleException(); + // fp_ used before params are known (e.g., in decltype return type) + out = (elementNum == 0) ? "fp" : "fp" + std::to_string(elementNum - 1); + break; } type = m_functionSubstitute[listNumber][elementNum]; } else if (isdigit(elm) || isupper(elm)) { elementNum = DemangleNumber() + 1; - if (m_reader.Read() != '_' || - elementNum < 0 || + if (m_reader.Read() != '_') + throw DemangleException(); + if (elementNum < 0 || + (uint64_t)listNumber >= (uint64_t)m_functionSubstitute.size() || (size_t)elementNum >= m_functionSubstitute[listNumber].size()) { - throw DemangleException(); + // fpN_ used before params are known + out = "fp" + std::to_string(elementNum - 1); + break; } type = m_functionSubstitute[listNumber][elementNum]; } @@ -1669,10 +1786,17 @@ string DemangleGNU3::DemangleExpression() if (m_reader.Peek() == 'N') { m_reader.Consume(); - out += DemangleUnresolvedType().GetString() + "::"; + // Standard form: N <unresolved-type> <qualifier-levels>+ E <base> + // where <unresolved-type> is T_, Dt, or S. + // GCC extension: N <source-name-qualifier>+ E <base> + // When the first component is a digit (source name), skip the + // unresolved-type and let the loop below handle all qualifiers. + if (!isdigit(m_reader.Peek())) + out += DemangleUnresolvedType().GetString() + "::"; do { out += DemangleSourceName(); + // Push bare name (before template args) to substitution table. PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, _STD_VECTOR<_STD_STRING>{out})); if (m_reader.Peek() == 'I') { @@ -1681,6 +1805,8 @@ string DemangleGNU3::DemangleExpression() //<tmplate-args> DemangleTemplateArgs(args); out += GetTemplateString(args); + // Also push the template instantiation (name+args). + PushType(DemangledTypeNode::NamedType(UnknownNamedTypeClass, _STD_VECTOR<_STD_STRING>{out})); } out += "::"; }while (m_reader.Peek() != 'E'); @@ -1691,26 +1817,83 @@ string DemangleGNU3::DemangleExpression() } if (isdigit(m_reader.Peek())) { + // <unresolved-qualifier-level>+ E <base-unresolved-name> + // GCC sometimes omits the explicit qualifier-list 'E' when the last + // qualifier ends with template-args (the template-args 'E' serves double + // duty). Break out of the loop immediately after any qualifier with + // template-args rather than waiting for a standalone 'E'. + // + // Each qualifier level adds to the substitution table: + // - the bare name (before template-args) as a substitution candidate + // - the template instantiation (name + args) as another candidate + // This mirrors how the compiler builds the substitution table during encoding. + bool hadTemplateArgs = false; do { - out += DemangleSourceName(); + hadTemplateArgs = false; + const string segName = DemangleSourceName(); + out += segName; + // Push bare name to substitution table. + PushType(CreateUnknownType(out)); if (m_reader.Peek() == 'I') { vector<string> args; m_reader.Consume(); - //<tmplate-args> - DemangleTemplateArgs(args); + DemangleTemplateArgs(args); // consumes the trailing 'E' out += GetTemplateString(args); + // Also push the template instantiation. + PushType(CreateUnknownType(out)); + hadTemplateArgs = true; } out += "::"; - }while (m_reader.Peek() != 'E'); - m_reader.Consume(); + }while (!hadTemplateArgs && m_reader.Peek() != 'E'); + // Consume qualifier-list 'E' if present. GCC sometimes omits it when + // the last qualifier had template-args whose 'E' served double duty, + // so check rather than unconditionally consuming. + if (m_reader.Peek() == 'E') + m_reader.Consume(); out += DemangleBaseUnresolvedName().GetString(); return out; } else { out += DemangleUnresolvedType().GetString() + "::"; + // GCC may encode multi-level scoped names without the 'N' qualifier + // prefix, e.g. "sr St 6__and_I<T>E 5value" for std::__and_<T>::value. + // Process any digit-started names: if a name has template args AND + // another source name follows, it is an intermediate qualifier level; + // otherwise it is the final base-unresolved-name. + while (isdigit(m_reader.Peek())) + { + const string segName = DemangleSourceName(); + if (m_reader.Peek() == 'I') + { + vector<string> args; + m_reader.Consume(); + DemangleTemplateArgs(args); + if (isdigit(m_reader.Peek())) + { + // Another source name follows — intermediate qualifier. + // Push to the substitution table, mirroring what the + // N-prefix sr branch does for each nested qualifier. + PushType(CreateUnknownType(out + segName + GetTemplateString(args))); + out += segName + GetTemplateString(args) + "::"; + } + else + { + // No more source names — this template-id is the final name. + out += segName + GetTemplateString(args); + return out; + } + } + else + { + // Plain source name with no template args — final base name. + out += segName; + return out; + } + } + // peek is not a digit: fall back for operator-names ("on") / destructor-names ("dn"). out += DemangleBaseUnresolvedName().GetString(); } return out; @@ -1731,7 +1914,7 @@ string DemangleGNU3::DemangleExpression() } -void DemangleGNU3::DemangleTemplateArgs(vector<string>& args) +void DemangleGNU3::DemangleTemplateArgs(vector<string>& args, bool* hadNonTypeArg) { indent(); MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); @@ -1749,15 +1932,23 @@ void DemangleGNU3::DemangleTemplateArgs(vector<string>& args) args.push_back(expr); tmp = CreateUnknownType(expr); tmpValid = true; + if (hadNonTypeArg) *hadNonTypeArg = true; break; case 'X': args.push_back(DemangleExpression()); if (m_reader.Read() != 'E') throw DemangleException(); + if (hadNonTypeArg) *hadNonTypeArg = true; break; + case 'I': // GCC sometimes uses I...E for argument packs instead of J...E case 'J': + { + size_t prevTemplateSize = m_templateSubstitute.size(); DemangleTemplateArgs(args); + if (m_topLevel && m_templateSubstitute.size() == prevTemplateSize) + PushTemplateType(CreateUnknownType("auto")); break; + } default: m_reader.UnRead(); topLevel = m_topLevel; @@ -1780,7 +1971,7 @@ void DemangleGNU3::DemangleTemplateArgs(vector<string>& args) } -DemangledTypeNode DemangleGNU3::DemangleNestedName() +DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs) { /* This can be either a qualified name like: "foo::bar::bas" @@ -1861,7 +2052,16 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName() if (!base) throw DemangleException(); vector<string> args; - DemangleTemplateArgs(args); + bool hadNonType = false; + DemangleTemplateArgs(args, allTypeTemplateArgs ? &hadNonType : nullptr); + if (allTypeTemplateArgs) + *allTypeTemplateArgs = !hadNonType; + // Resolve any forward template refs created while parsing a cv + // conversion operator type (e.g. cv T_ where T_ wasn't yet known). + // Only do this in the outer context (not while still inside the cv + // type parsing itself where m_permitForwardTemplateRefs is true). + if (!m_permitForwardTemplateRefs) + ResolveForwardTemplateRefs(type, args); ExtendTypeName(type, GetTemplateString(args)); type.SetHasTemplateArguments(true); isTemplate = true; @@ -1942,10 +2142,21 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() MyLogDebug("%s '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str()); DemangledTypeNode type; QualifiedName varName; + // The local function has its own template scope. Save the outer template + // substitution table and set m_topLevel = true so that when the local + // function's template args are parsed (e.g. handleMessageDelayed<T, T0, T1>), + // they populate m_templateSubstitute and are available for T_/T0_/T1_ + // references in the function's parameter types. + auto savedTemplateSubstitute = m_templateSubstitute; + m_templateSubstitute.clear(); bool oldTopLevel = m_topLevel; - m_topLevel = false; + m_topLevel = true; + bool savedInLocalName = m_inLocalName; + m_inLocalName = true; type = DemangleSymbol(varName); + m_inLocalName = savedInLocalName; m_topLevel = oldTopLevel; + m_templateSubstitute = std::move(savedTemplateSubstitute); if (varName.size() > 0) varName.back() += (type.GetStringAfterName()); @@ -1954,6 +2165,15 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName() if (m_reader.Peek() != 's') { + // Handle default argument context: d [<number>] _ <name> + if (m_reader.Peek() == 'd') + { + m_reader.Consume(); + if (isdigit(m_reader.Peek())) + DemangleNumber(); + if (m_reader.Peek() == '_') + m_reader.Consume(); + } //<entity name> DemangledTypeNode tmpType = DemangleName(); type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName); @@ -2041,11 +2261,21 @@ DemangledTypeNode DemangleGNU3::DemangleName() DemangleTemplateArgs(args); ExtendTypeName(type, GetTemplateString(args)); type.SetHasTemplateArguments(true); + // Push the template instantiation (e.g. std::swap<T>) so that the + // substitution table matches what the encoder built. The encoder adds + // both the unscoped-template-name (prefix, already pushed above) and + // the full template-id (instantiation). + PushType(type); } break; case 'N': //<nested-name> - type = DemangleNestedName(); + { + bool allTypeArgs = false; + type = DemangleNestedName(&allTypeArgs); + if (!m_inLocalName && allTypeArgs) + PushType(type); break; + } case 'Z': //<local-name> type = DemangleLocalName(); break; @@ -2211,6 +2441,8 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(QualifiedName& varName) return type; } + + // Intel Vector Function ABI: // GV <isa> <mask> <vlen> <vparameters> '_' <routine_name> @@ -2536,12 +2768,12 @@ DemangledTypeNode DemangleGNU3::DemangleSymbol(QualifiedName& varName) // On the off chance some invalid mangled string is passed in. if (varName.size() > 0) - varName.back() += ext; + varName.back() += " " + ext; break; } m_isParameter = true; - MyLogDebug("Var_%d: %s\n", i, m_reader.GetRaw().c_str()); + MyLogDebug("Var: %s\n", m_reader.GetRaw().c_str()); if (m_reader.PeekString(2) == "@@") break; DemangledTypeNode param = DemangleType(); diff --git a/demangler/gnu3/demangle_gnu3.h b/demangler/gnu3/demangle_gnu3.h index 2e46e05d..8ee0fca6 100644 --- a/demangler/gnu3/demangle_gnu3.h +++ b/demangler/gnu3/demangle_gnu3.h @@ -51,10 +51,10 @@ public: DemangleGNU3Reader(const _STD_STRING& data); void Reset(const _STD_STRING& data); _STD_STRING PeekString(size_t count=1); - bool NextIsOneOf(const _STD_STRING& list); +#ifdef GNUDEMANGLE_DEBUG _STD_STRING GetRaw(); +#endif _STD_STRING ReadString(size_t count=1); - _STD_STRING ReadUntil(char sentinal); size_t Length() const { return m_data.length() - m_offset; } @@ -109,6 +109,17 @@ class DemangleGNU3 bool m_shouldDeleteReader; bool m_topLevel; bool m_isOperatorOverload; + // Forward template reference support (for cv conversion operator types). + // When m_permitForwardTemplateRefs is true, DemangleTemplateSubstitution() + // returns a placeholder instead of throwing for out-of-bounds template params. + // m_pendingForwardRefs records which param indices have placeholders so that + // ResolveForwardTemplateRefs() can patch them once template args are known. + bool m_permitForwardTemplateRefs; + bool m_inLocalName; + struct ForwardRef { size_t index; }; + _STD_VECTOR<ForwardRef> m_pendingForwardRefs; + void ResolveForwardTemplateRefs(DemangledTypeNode& type, const _STD_VECTOR<_STD_STRING>& args); + static _STD_STRING ForwardRefPlaceholder(size_t index); enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name}; BN::QualifiedName DemangleBaseUnresolvedName(); DemangledTypeNode DemangleUnresolvedType(); @@ -121,7 +132,6 @@ class DemangleGNU3 DemangledTypeNode DemangleUnqualifiedName(); _STD_STRING DemangleSourceName(); _STD_STRING DemangleNumberAsString(); - _STD_STRING DemangleInitializer(); _STD_STRING DemangleExpression(); _STD_STRING DemanglePrimaryExpression(); DemangledTypeNode DemangleName(); @@ -129,14 +139,13 @@ class DemangleGNU3 void DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct); DemangledTypeNode DemangleSubstitution(); - const DemangledTypeNode& DemangleTemplateSubstitution(); - void DemangleTemplateArgs(_STD_VECTOR<_STD_STRING>& args); + DemangledTypeNode DemangleTemplateSubstitution(); + void DemangleTemplateArgs(_STD_VECTOR<_STD_STRING>& args, bool* hadNonTypeArg = nullptr); DemangledTypeNode DemangleFunction(bool cnst, bool vltl); DemangledTypeNode DemangleType(); int64_t DemangleNumber(); - DemangledTypeNode DemangleNestedName(); + DemangledTypeNode DemangleNestedName(bool* allTypeTemplateArgs = nullptr); void PushTemplateType(const DemangledTypeNode& type); - const DemangledTypeNode& GetTemplateType(size_t ref); void PushType(const DemangledTypeNode& type); const DemangledTypeNode& GetType(size_t ref); @@ -144,12 +153,16 @@ class DemangleGNU3 DemangledTypeNode CreateUnknownType(const _STD_STRING& s); static void ExtendTypeName(DemangledTypeNode& type, const _STD_STRING& extend); +#ifdef GNUDEMANGLE_DEBUG + const DemangledTypeNode& GetTemplateType(size_t ref); + void PrintTables(); +#endif + public: DemangleGNU3(BN::Architecture* arch, const _STD_STRING& mangledName); void Reset(BN::Architecture* arch, const _STD_STRING& mangledName); DemangledTypeNode DemangleSymbol(BN::QualifiedName& varName); BN::QualifiedName GetVarName() const { return m_varName; } - void PrintTables(); }; diff --git a/demangler/gnu3/demangled_type_node.cpp b/demangler/gnu3/demangled_type_node.cpp index d8a98e16..89bfb2fa 100644 --- a/demangler/gnu3/demangled_type_node.cpp +++ b/demangler/gnu3/demangled_type_node.cpp @@ -63,11 +63,12 @@ DemangledTypeNode DemangledTypeNode::IntegerType(size_t width, bool isSigned, co } -DemangledTypeNode DemangledTypeNode::FloatType(size_t width) +DemangledTypeNode DemangledTypeNode::FloatType(size_t width, const string& altName) { DemangledTypeNode n; n.m_typeClass = FloatTypeClass; n.m_width = width; + n.m_altName = altName; return n; } @@ -274,7 +275,9 @@ void DemangledTypeNode::AppendBeforeName(string& out, const DemangledTypeNode* p break; case FloatTypeClass: - switch (m_width) + if (!m_altName.empty()) + out += m_altName; + else switch (m_width) { case 2: out += "float16"; break; case 4: out += "float"; break; @@ -464,8 +467,8 @@ Ref<Type> DemangledTypeNode::Finalize() const case FloatTypeClass: { if (!m_const && !m_volatile) - return Type::FloatType(m_width); - TypeBuilder tb = TypeBuilder::FloatType(m_width); + return Type::FloatType(m_width, m_altName); + TypeBuilder tb = TypeBuilder::FloatType(m_width, m_altName); tb.SetConst(m_const); tb.SetVolatile(m_volatile); return tb.Finalize(); diff --git a/demangler/gnu3/demangled_type_node.h b/demangler/gnu3/demangled_type_node.h index f272166c..62ad9004 100644 --- a/demangler/gnu3/demangled_type_node.h +++ b/demangler/gnu3/demangled_type_node.h @@ -72,7 +72,7 @@ public: static DemangledTypeNode VoidType(); static DemangledTypeNode BoolType(); static DemangledTypeNode IntegerType(size_t width, bool isSigned, const _STD_STRING& altName = ""); - static DemangledTypeNode FloatType(size_t width); + static DemangledTypeNode FloatType(size_t width, const _STD_STRING& altName = ""); static DemangledTypeNode VarArgsType(); static DemangledTypeNode PointerType(BN::Architecture* arch, DemangledTypeNode child, bool cnst, bool vltl, BNReferenceType refType); |
