From a9939fb35dc141f6ab09e2aad88091c7e2bffcac Mon Sep 17 00:00:00 2001 From: ghizard <50744617+ghizard@users.noreply.github.com> Date: Tue, 14 Apr 2026 05:51:08 -0400 Subject: [PATCH] GP-6703 - MDMang template const research and other minor tweaks --- .../java/mdemangler/MDCharacterIterator.java | 6 ++ .../java/mdemangler/naming/MDSpecialName.java | 11 +- .../template/MDTemplateConstant.java | 100 ++++++++++++++---- .../test/java/mdemangler/MDMangBaseTest.java | 86 +++++++++++++++ 4 files changed, 176 insertions(+), 27 deletions(-) diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java index 7c8746409c..a2df90cada 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDCharacterIterator.java @@ -192,4 +192,10 @@ public class MDCharacterIterator { public boolean positionStartsWith(String substring) { return string.regionMatches(index, substring, 0, substring.length()); } + + @Override + public String toString() { + return string + "\n" + " ".repeat(index) + "^"; + } + } diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDSpecialName.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDSpecialName.java index 4dfd4aab41..43b4cf492f 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDSpecialName.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDSpecialName.java @@ -637,7 +637,8 @@ public class MDSpecialName extends MDParsableItem { } break; case 'F': { - dmang.parseInfoPush(3, "dynamic atexit destructor operator"); + dmang.parseInfoPush(3, + "dynamic atexit destructor operator for [Object]"); StringBuilder builder = new StringBuilder(); // TODO... 20160812: This looks like new ObjectCPP and ObjectC if (dmang.peek() == '?') { @@ -672,17 +673,17 @@ public class MDSpecialName extends MDParsableItem { } break; case 'G': - dmang.parseInfoPush(3, "vector copy constructor"); + dmang.parseInfoPush(3, "vector copy constructor iterator"); name = "`vector copy constructor iterator'"; dmang.parseInfoPop(); break; case 'H': - dmang.parseInfoPush(3, "vector vbase copy constructor"); + dmang.parseInfoPush(3, "vector vbase copy constructor iterator"); name = "`vector vbase copy constructor iterator'"; dmang.parseInfoPop(); break; case 'I': - dmang.parseInfoPush(3, "managed vector copy constructor"); + dmang.parseInfoPush(3, "managed vector copy constructor iterator"); name = "`managed vector copy constructor iterator'"; dmang.parseInfoPop(); break; @@ -692,7 +693,7 @@ public class MDSpecialName extends MDParsableItem { dmang.parseInfoPop(); break; case 'K': - dmang.parseInfoPush(3, "udl"); + dmang.parseInfoPush(3, "user defined literal"); // Our test has manufactured symbol "??__Kabc@def@@3HA" // 20170329: Confirmed that this is not a MDReusableName with // manufactured symbol "??__Kabc@def@0@3HA" diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/template/MDTemplateConstant.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/template/MDTemplateConstant.java index 3673d709a5..e8cf7b8060 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/template/MDTemplateConstant.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/template/MDTemplateConstant.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,9 +16,13 @@ package mdemangler.template; import mdemangler.*; +import mdemangler.datatype.MDDataType; +import mdemangler.datatype.MDDataTypeParser; import mdemangler.naming.MDFragmentName; import mdemangler.object.MDObject; import mdemangler.object.MDObjectCPP; +import mdemangler.typeinfo.MDTypeInfo; +import mdemangler.typeinfo.MDTypeInfoParser; /** * This class represents a the template constant portion of a @@ -66,24 +70,17 @@ public class MDTemplateConstant extends MDParsableItem { // name = "&" + object; } break; - case '2': // TODO: Currently, we have no test data for this. - a = new MDSignedEncodedNumber(dmang); - a.parse(); - String aStr = a.toString(); - // 20140630: seems that '\'' is not there in $2a'b (documentation - // notation failure--the single quote (any type) is not really in the - // mangled string) - b = new MDSignedEncodedNumber(dmang); - b.parse(); - if (aStr.charAt(0) == '-') { - name = "-"; - aStr = aStr.substring(1, aStr.length()); + case '2': + // This is not perect. The newer encoding might not get tripped + // up by attempting to demangle with the older scheme or vice versa. + int currentIndex = dmang.getIndex(); + try { + name = parseFloatingPointConstant(); } - name += aStr.charAt(0) + "."; - if (aStr.length() > 1) { - name += aStr.substring(1, aStr.length()); + catch (MDException e) { + dmang.setIndex(currentIndex); + name = parseInitializedTemplateConstant(); } - name += "e" + b; break; case 'D': a = new MDSignedEncodedNumber(dmang); @@ -102,7 +99,7 @@ public class MDTemplateConstant extends MDParsableItem { case 'F': // 20140630: seems that '\'' is not there in $Fa'b (documentation // notation failure--the single quote (any type) is not really in the - // mangled string) + // mangled string) a = new MDSignedEncodedNumber(dmang); a.parse(); b = new MDSignedEncodedNumber(dmang); @@ -112,7 +109,7 @@ public class MDTemplateConstant extends MDParsableItem { case 'G': // 20140630: seems that '\'' is not there in $Ga'b'c (documentation // notation failure--the single quote (any type) is not really in the - // mangled string) + // mangled string) a = new MDSignedEncodedNumber(dmang); a.parse(); b = new MDSignedEncodedNumber(dmang); @@ -140,7 +137,7 @@ public class MDTemplateConstant extends MDParsableItem { case 'I': { // Used in some cases ($$issue!!!!!!) // 20140630: seems that '\'' is not there in $Ix'y (documentation // notation failure--the single quote (any type) is not really in the - // mangled string) + // mangled string) // It also seems that the second parameter CAN be negative: $Ixa, where // x is some object, and a is some signed number // Do not do a pushModifierContext here. @@ -165,7 +162,7 @@ public class MDTemplateConstant extends MDParsableItem { case 'J': { // Used in some cases ($$issue!!!!!!) // 20140630: seems that '\'' is not there in $Jx'y'z (documentation // notation failure--the single quote (any type) is not really in the - // mangled string) + // mangled string) // It also seems that the second, third, and fourth parameters CAN be // negative: $Jxabc, where x is some object, and a, b, and care some // signed numbers @@ -221,6 +218,65 @@ public class MDTemplateConstant extends MDParsableItem { } } + private String parseFloatingPointConstant() throws MDException { + String str = ""; + MDSignedEncodedNumber a = new MDSignedEncodedNumber(dmang); + a.parse(); + String aStr = a.toString(); + // 20140630: seems that '\'' is not there in $2a'b (documentation + // notation failure--the single quote (any type) is not really in the + // mangled string) + MDSignedEncodedNumber b = new MDSignedEncodedNumber(dmang); + b.parse(); + if (aStr.charAt(0) == '-') { + str = "-"; + aStr = aStr.substring(1, aStr.length()); + } + str += aStr.charAt(0) + "."; + if (aStr.length() > 1) { + str += aStr.substring(1, aStr.length()); + } + str += "e" + b; + return str; + } + + // Not sure that I like the name of this method, as I'm not sure what the full scope of + // encodings/initializations are at this time. + private String parseInitializedTemplateConstant() throws MDException { + String str = ""; + // I don't know if we need to push and pop context before and after this + // (to have correct back references if needed). + MDDataType t = MDDataTypeParser.parseDataType(dmang, true); + t.parse(); + + str += t.toString() + "{"; + + char code = dmang.peek(); + while (code != '@') { + //From MDDataTypeParser: +// MDParsableItem item = new MDObjectCPP(dmang); +// dmang.pushContext(); +// item.parse(); +// dmang.popContext(); + + // I don't know if we need to push and pop context before and after this + // (to have correct back references if needed). + + //From MDObjectCpp + //int RTTINum = qualifiedName.getRTTINumber(); + MDTypeInfo typeInfo = MDTypeInfoParser.parse(dmang, -1); + //if (qualifiedName.isTypeCast()) { + // typeInfo.setTypeCast(); + //} + typeInfo.parse(); + + str += typeInfo.toString(); + } + dmang.next(); // skip the '@' character + str += "}"; + return str; + } + @Override public void insert(StringBuilder builder) { dmang.insertString(builder, name); diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java index 98d42c92c1..99be154a2a 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java @@ -3243,6 +3243,26 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + //===================== + // $2 template constants + // + // We seem to have two different encodings for $2 template constant. An older form that + // encodes floating point numbers that works in undname from vs2017 and a newer form that + // encodes type constants with initializers that works in undname from vs2022. The 2017 + // mangled strings do not demangle under 2022 and the 2022 strings do not demangle under + // 2017. I have not checked other VS versions. Probably no longer need to encode floating + // point type the same way, as they could probably be encoded with the newer format, which + // should offer a broader encoding scheme. We don't know if both encoding styles can + // coexist or whether we need to offer different demangling versions (e.g., see MDMangVS2015). + // We haven't pursued fleshing out the differences between versions, but if we did, then we + // could offer the user the options of choosing or we could detect from PE header, PDB, or + // other analysis. Probably best if we can make all coexist. (Note: we still have other + // issues where LLVM or Clang don't adhere exactly to some MSFT encoding standards; this + // was partially worked around, but is still a mess.) + // + // ALSO... gaining evidence that there are many more $ codes for template constants than + // what we have implemented. Seems like it might be a lot of work to tackle all of them. + //manufactured symbol: $2 Blank (zero) exponent @Test public void testSpecialTemplateParameters_dollar2_blankzeroexp() throws Exception { @@ -3366,6 +3386,56 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + // Newer $2 template constant encodings below. See note at top of $2 section + + // real symbol with $2 non-floating point template constant with initializers + // From template_op_lit/tol64 + // mstruth here is from vs2022 undname + @Category(MDMangFailingTestCategory.class) + @Test + public void testTemplateConstantParameterWithInitializers1() throws Exception { + mangled = + "??$?__K_x2@$2U?$DoubleString@$03@@3D0GB@@0GC@@0GD@@0GB@@0GC@@0GD@@0A@@@@@@YA?A_PXZ"; + msTruth = + "auto __cdecl operator \"\" _x2{char{97,98,99,97,98,99,0}}>(void)"; + mdTruth = msTruth; + demangleAndTest16Bit(); + } + + // This is fuzzed counterpoint to the real $2 template constant, above + // mstruth here is from vs2022 undname + @Category(MDMangFailingTestCategory.class) + @Test + public void testTemplateConstantParameterWithInitializers1_counterpoint1() throws Exception { + mangled = "??$?M$2H@@@YA?AXXZ"; + msTruth = "void __cdecl operator<(void)"; + mdTruth = msTruth; + demangleAndTest16Bit(); + } + + // This is fuzzed counterpoint to the real $2 template constant, above. This does not have $2 + // mstruth here is from both vs2022 and vs2017 undname + @Test + public void testTemplateConstantParameterWithInitializers1_counterpoint2() throws Exception { + mangled = "??$?MH@@YAXXZ"; + msTruth = "void __cdecl operator<(void)"; + mdTruth = msTruth; + demangleAndTest16Bit(); + } + + // This is fuzzed counterpoint to the real $2 template constant, above. This does not have $2 + // probably don't need since we just figured out ?A (we already process) + // mstruth here is from both vs2022 and vs2017 undname + @Test + public void testTemplateConstantParameterWithInitializers1_counterpoint3() throws Exception { + mangled = "??$?MH@@YA?AXXZ"; + msTruth = "void __cdecl operator<(void)"; + mdTruth = msTruth; + demangleAndTest16Bit(); + } + + //===================== + //real symbol: $D @Test public void testSpecialTemplateParameters_dollarD() throws Exception { @@ -5050,6 +5120,22 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + //manufactured symbol + // We have no real symbols and haven't been able to get undname to produce results, + // which means that we might not have all the right pieces for a ??_W symbol to parse. + // If we find/produce a symbol that undname works on, the we should delete and + // replace this test and change the demangler to properly parse the symbol (which should + // also cause this current test to fail). + @Test + public void testSpecialNames_W() throws Exception { + mangled = "??_WAA@@QAAXXZ"; + msTruth = ""; + mdTruth = "public: void __cdecl AA::`omni callsig'(void)"; + demangleAndTest(); + } + // ??_WAA@@QAAXXZ + // public: void __cdecl AA::`omni callsig'(void) + //manufactured symbol @Test public void testSpecialNames_X() throws Exception {