Skip to content

Commit d0c36d7

Browse files
plafosseclaude
andcommitted
Fix GNU3 demangler substitution table and decltype handling
- Increase MAX_DEMANGLE_LENGTH to 262144 (256KB) to handle deeply templated symbols from real-world binaries - Fix substitution table off-by-one for template functions whose template args are all types (no non-type L/X args): DemangleName's 'N' case now pushes the complete specialized qualified name to the substitution table, matching the compiler's encoding. Introduced DemangleTemplateArgs(hadNonTypeArg) and DemangleNestedName(allTypeTemplateArgs) out-parameters to detect when all template args are types and the push is appropriate. - Fix DemangleName's 'Z' (local-name) context: added m_inLocalName flag set around the inner DemangleSymbol call in DemangleLocalName, so the push only fires for the outer function name, not inner scope functions. - Fix DemangleUnresolvedType decltype handling: the DT/Dt branch was peeking for the prefix but not consuming it before calling DemangleExpression, causing the expression parser to see 'D','T' with no matching case. Now correctly consumes the two-char prefix and the mandatory trailing 'E', enabling sr(decltype(...))::member patterns. - Add forward template ref support for cv conversion operator types: ResolveForwardTemplateRefs patches placeholder types in the result once the enclosing template-args are known. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 899f830 commit d0c36d7

2 files changed

Lines changed: 144 additions & 21 deletions

File tree

demangler/gnu3/demangle_gnu3.cpp

Lines changed: 130 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ using namespace std;
3030
#endif
3131

3232

33-
#define MAX_DEMANGLE_LENGTH 4096
33+
#define MAX_DEMANGLE_LENGTH 262144
3434
#define hash(x,y) (64 * x + y)
3535

3636
#undef GNUDEMANGLE_DEBUG
@@ -308,7 +308,8 @@ DemangleGNU3::DemangleGNU3(Architecture* arch, const string& mangledName) :
308308
m_isParameter(false),
309309
m_shouldDeleteReader(true),
310310
m_topLevel(true),
311-
m_isOperatorOverload(false)
311+
m_isOperatorOverload(false),
312+
m_permitForwardTemplateRefs(false)
312313
{
313314
MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str());
314315
}
@@ -330,6 +331,9 @@ void DemangleGNU3::Reset(Architecture* arch, const string& mangledName)
330331
m_shouldDeleteReader = true;
331332
m_topLevel = true;
332333
m_isOperatorOverload = false;
334+
m_permitForwardTemplateRefs = false;
335+
m_pendingForwardRefs.clear();
336+
m_inLocalName = false;
333337
}
334338

335339

@@ -369,10 +373,7 @@ void DemangleGNU3::PushTemplateType(const DemangledTypeNode& type)
369373
const DemangledTypeNode& DemangleGNU3::GetTemplateType(size_t ref)
370374
{
371375
if (ref >= m_templateSubstitute.size())
372-
{
373-
// PrintTables();
374376
throw DemangleException();
375-
}
376377
return m_templateSubstitute[ref];
377378
}
378379

@@ -386,10 +387,7 @@ void DemangleGNU3::PushType(const DemangledTypeNode& type)
386387
const DemangledTypeNode& DemangleGNU3::GetType(size_t ref)
387388
{
388389
if (ref >= m_substitute.size())
389-
{
390-
// PrintTables();
391390
throw DemangleException();
392-
}
393391
return m_substitute[ref];
394392
}
395393

@@ -482,7 +480,43 @@ DemangledTypeNode DemangleGNU3::DemangleFunction(bool cnst, bool vltl)
482480
}
483481

484482

485-
const DemangledTypeNode& DemangleGNU3::DemangleTemplateSubstitution()
483+
string DemangleGNU3::ForwardRefPlaceholder(size_t index)
484+
{
485+
return "\x01FWDREF:" + to_string(index) + "\x01";
486+
}
487+
488+
489+
void DemangleGNU3::ResolveForwardTemplateRefs(DemangledTypeNode& type, const vector<string>& args)
490+
{
491+
if (m_pendingForwardRefs.empty())
492+
return;
493+
auto& segs = type.GetMutableTypeName();
494+
bool resolved = false;
495+
for (const auto& fr : m_pendingForwardRefs)
496+
{
497+
string placeholder = ForwardRefPlaceholder(fr.index);
498+
string replacement = (fr.index < args.size()) ? args[fr.index] : "auto";
499+
for (auto& seg : segs)
500+
{
501+
size_t pos;
502+
while ((pos = seg.find(placeholder)) != string::npos)
503+
{
504+
seg.replace(pos, placeholder.size(), replacement);
505+
resolved = true;
506+
}
507+
}
508+
}
509+
// Only clear the pending list when we actually resolved something. Inner
510+
// nested-name 'I' handlers (e.g. template args of types nested inside the
511+
// cv-operator result type) may call here with a type that does not contain
512+
// the placeholder; we must not discard the pending entry in that case so
513+
// that the correct outer 'I' handler can still resolve it.
514+
if (resolved)
515+
m_pendingForwardRefs.clear();
516+
}
517+
518+
519+
DemangledTypeNode DemangleGNU3::DemangleTemplateSubstitution()
486520
{
487521
indent();
488522
MyLogDebug("%s : %s\n", __FUNCTION__, m_reader.GetRaw().c_str());
@@ -512,7 +546,20 @@ const DemangledTypeNode& DemangleGNU3::DemangleTemplateSubstitution()
512546
throw DemangleException();
513547
}
514548
dedent();
515-
return GetTemplateType(number);
549+
550+
if (number < m_templateSubstitute.size())
551+
return m_templateSubstitute[number];
552+
553+
// If forward template references are permitted (e.g. inside a cv conversion
554+
// operator type), return a placeholder that will be resolved once the outer
555+
// template args are known.
556+
if (m_permitForwardTemplateRefs)
557+
{
558+
m_pendingForwardRefs.push_back({number});
559+
return CreateUnknownType(ForwardRefPlaceholder(number));
560+
}
561+
562+
throw DemangleException();
516563
}
517564

518565

@@ -615,8 +662,11 @@ DemangledTypeNode DemangleGNU3::DemangleType()
615662

616663
//Template Substitution
617664
type = DemangleTemplateSubstitution();
618-
substitute = true;
619-
if (m_reader.Peek() == 'I')
665+
// In forward-ref mode (cv conversion operator type parsing), do not consume
666+
// trailing I<args>E — it belongs to the enclosing nested-name and will be
667+
// processed by DemangleNestedName's 'I' case, which resolves forward refs.
668+
substitute = !m_permitForwardTemplateRefs;
669+
if (!m_permitForwardTemplateRefs && m_reader.Peek() == 'I')
620670
{
621671
m_reader.Consume();
622672
if (substitute)
@@ -884,7 +934,11 @@ DemangledTypeNode DemangleGNU3::DemangleSubstitution()
884934
}
885935

886936
dedent();
887-
return GetType(number);
937+
const DemangledTypeNode& resolved = GetType(number);
938+
const auto& segs = resolved.GetTypeName();
939+
if (!segs.empty())
940+
m_lastName = segs.back();
941+
return resolved;
888942
}
889943
m_lastName = name.back();
890944
dedent();
@@ -1273,8 +1327,22 @@ DemangledTypeNode DemangleGNU3::DemangleUnqualifiedName()
12731327
break;
12741328
}
12751329
case hash('c','v'): //type (expression)
1276-
outType = CreateUnknownType("operator " + DemangleType().GetString());
1330+
{
1331+
// The conversion operator type may reference template params (T_, T0_, ...)
1332+
// that aren't yet in m_templateSubstitute (they're defined by a following
1333+
// I<args>E in the enclosing nested name). Set m_permitForwardTemplateRefs so
1334+
// that DemangleTemplateSubstitution() returns a placeholder instead of
1335+
// throwing, and don't consume trailing I<args>E in the T case of DemangleType.
1336+
// The outer DemangleNestedName case 'I' will parse those args and call
1337+
// ResolveForwardTemplateRefs() to patch the placeholders.
1338+
bool savedPermit = m_permitForwardTemplateRefs;
1339+
m_pendingForwardRefs.clear();
1340+
m_permitForwardTemplateRefs = true;
1341+
DemangledTypeNode cvType = DemangleType();
1342+
m_permitForwardTemplateRefs = savedPermit;
1343+
outType = CreateUnknownType("operator " + cvType.GetString());
12771344
break;
1345+
}
12781346
default:
12791347
m_reader.UnRead(2);
12801348
if (isdigit(m_reader.Peek()) || m_reader.Read() == 'L')
@@ -1368,10 +1436,21 @@ DemangledTypeNode DemangleGNU3::DemangleUnresolvedType()
13681436
type.SetHasTemplateArguments(true);
13691437
PushType(type);
13701438
}
1439+
else
1440+
{
1441+
// Template param used as scope qualifier (e.g. sr T_ name) is a substitution
1442+
// candidate: the compiler adds it to the main sub table so subsequent
1443+
// occurrences can use Sn_ instead of T_.
1444+
PushType(type);
1445+
}
13711446
}
13721447
else if (m_reader.Length() > 2 && (m_reader.PeekString(2) == "Dt" || m_reader.PeekString(2) == "DT"))
13731448
{
1449+
m_reader.Consume(); // 'D'
1450+
m_reader.Consume(); // 't' or 'T'
13741451
const string name = "decltype(" + DemangleExpression() + ")";
1452+
if (m_reader.Read() != 'E')
1453+
throw DemangleException();
13751454
type = CreateUnknownType(name);
13761455
}
13771456
else if (m_reader.Peek() == 'S')
@@ -1731,7 +1810,7 @@ string DemangleGNU3::DemangleExpression()
17311810
}
17321811

17331812

1734-
void DemangleGNU3::DemangleTemplateArgs(vector<string>& args)
1813+
void DemangleGNU3::DemangleTemplateArgs(vector<string>& args, bool* hadNonTypeArg)
17351814
{
17361815
indent();
17371816
MyLogDebug("%s:: '%s'\n", __FUNCTION__, m_reader.GetRaw().c_str());
@@ -1749,16 +1828,23 @@ void DemangleGNU3::DemangleTemplateArgs(vector<string>& args)
17491828
args.push_back(expr);
17501829
tmp = CreateUnknownType(expr);
17511830
tmpValid = true;
1831+
if (hadNonTypeArg) *hadNonTypeArg = true;
17521832
break;
17531833
case 'X':
17541834
args.push_back(DemangleExpression());
17551835
if (m_reader.Read() != 'E')
17561836
throw DemangleException();
1837+
if (hadNonTypeArg) *hadNonTypeArg = true;
17571838
break;
17581839
case 'I': // GCC sometimes uses I...E for argument packs instead of J...E
17591840
case 'J':
1841+
{
1842+
size_t prevTemplateSize = m_templateSubstitute.size();
17601843
DemangleTemplateArgs(args);
1844+
if (m_topLevel && m_templateSubstitute.size() == prevTemplateSize)
1845+
PushTemplateType(CreateUnknownType("auto"));
17611846
break;
1847+
}
17621848
default:
17631849
m_reader.UnRead();
17641850
topLevel = m_topLevel;
@@ -1781,7 +1867,7 @@ void DemangleGNU3::DemangleTemplateArgs(vector<string>& args)
17811867
}
17821868

17831869

1784-
DemangledTypeNode DemangleGNU3::DemangleNestedName()
1870+
DemangledTypeNode DemangleGNU3::DemangleNestedName(bool* allTypeTemplateArgs)
17851871
{
17861872
/*
17871873
This can be either a qualified name like: "foo::bar::bas"
@@ -1858,7 +1944,16 @@ DemangledTypeNode DemangleGNU3::DemangleNestedName()
18581944
if (!base)
18591945
throw DemangleException();
18601946
vector<string> args;
1861-
DemangleTemplateArgs(args);
1947+
bool hadNonType = false;
1948+
DemangleTemplateArgs(args, allTypeTemplateArgs ? &hadNonType : nullptr);
1949+
if (allTypeTemplateArgs)
1950+
*allTypeTemplateArgs = !hadNonType;
1951+
// Resolve any forward template refs created while parsing a cv
1952+
// conversion operator type (e.g. cv T_ where T_ wasn't yet known).
1953+
// Only do this in the outer context (not while still inside the cv
1954+
// type parsing itself where m_permitForwardTemplateRefs is true).
1955+
if (!m_permitForwardTemplateRefs)
1956+
ResolveForwardTemplateRefs(type, args);
18621957
ExtendTypeName(type, GetTemplateString(args));
18631958
type.SetHasTemplateArguments(true);
18641959
isTemplate = true;
@@ -1948,7 +2043,10 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName()
19482043
m_templateSubstitute.clear();
19492044
bool oldTopLevel = m_topLevel;
19502045
m_topLevel = true;
2046+
bool savedInLocalName = m_inLocalName;
2047+
m_inLocalName = true;
19512048
type = DemangleSymbol(varName);
2049+
m_inLocalName = savedInLocalName;
19522050
m_topLevel = oldTopLevel;
19532051
m_templateSubstitute = std::move(savedTemplateSubstitute);
19542052

@@ -1959,6 +2057,15 @@ DemangledTypeNode DemangleGNU3::DemangleLocalName()
19592057

19602058
if (m_reader.Peek() != 's')
19612059
{
2060+
// Handle default argument context: d [<number>] _ <name>
2061+
if (m_reader.Peek() == 'd')
2062+
{
2063+
m_reader.Consume();
2064+
if (isdigit(m_reader.Peek()))
2065+
DemangleNumber();
2066+
if (m_reader.Peek() == '_')
2067+
m_reader.Consume();
2068+
}
19622069
//<entity name>
19632070
DemangledTypeNode tmpType = DemangleName();
19642071
type = DemangledTypeNode::NamedType(UnknownNamedTypeClass, varName);
@@ -2049,8 +2156,13 @@ DemangledTypeNode DemangleGNU3::DemangleName()
20492156
}
20502157
break;
20512158
case 'N': //<nested-name>
2052-
type = DemangleNestedName();
2159+
{
2160+
bool allTypeArgs = false;
2161+
type = DemangleNestedName(&allTypeArgs);
2162+
if (!m_inLocalName && allTypeArgs)
2163+
PushType(type);
20532164
break;
2165+
}
20542166
case 'Z': //<local-name>
20552167
type = DemangleLocalName();
20562168
break;

demangler/gnu3/demangle_gnu3.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ class DemangleGNU3
110110
bool m_shouldDeleteReader;
111111
bool m_topLevel;
112112
bool m_isOperatorOverload;
113+
// Forward template reference support (for cv conversion operator types).
114+
// When m_permitForwardTemplateRefs is true, DemangleTemplateSubstitution()
115+
// returns a placeholder instead of throwing for out-of-bounds template params.
116+
// m_pendingForwardRefs records which param indices have placeholders so that
117+
// ResolveForwardTemplateRefs() can patch them once template args are known.
118+
bool m_permitForwardTemplateRefs;
119+
bool m_inLocalName;
120+
struct ForwardRef { size_t index; };
121+
_STD_VECTOR<ForwardRef> m_pendingForwardRefs;
122+
void ResolveForwardTemplateRefs(DemangledTypeNode& type, const _STD_VECTOR<_STD_STRING>& args);
123+
static _STD_STRING ForwardRefPlaceholder(size_t index);
113124
enum SymbolType { Function, FunctionWithReturn, Data, VTable, Rtti, Name};
114125
BN::QualifiedName DemangleBaseUnresolvedName();
115126
DemangledTypeNode DemangleUnresolvedType();
@@ -130,12 +141,12 @@ class DemangleGNU3
130141

131142
void DemangleCVQualifiers(bool& cnst, bool& vltl, bool& rstrct);
132143
DemangledTypeNode DemangleSubstitution();
133-
const DemangledTypeNode& DemangleTemplateSubstitution();
134-
void DemangleTemplateArgs(_STD_VECTOR<_STD_STRING>& args);
144+
DemangledTypeNode DemangleTemplateSubstitution();
145+
void DemangleTemplateArgs(_STD_VECTOR<_STD_STRING>& args, bool* hadNonTypeArg = nullptr);
135146
DemangledTypeNode DemangleFunction(bool cnst, bool vltl);
136147
DemangledTypeNode DemangleType();
137148
int64_t DemangleNumber();
138-
DemangledTypeNode DemangleNestedName();
149+
DemangledTypeNode DemangleNestedName(bool* allTypeTemplateArgs = nullptr);
139150
void PushTemplateType(const DemangledTypeNode& type);
140151
const DemangledTypeNode& GetTemplateType(size_t ref);
141152
void PushType(const DemangledTypeNode& type);

0 commit comments

Comments
 (0)