diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java index 8702e8a785..8dfda5c9a3 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java @@ -162,22 +162,10 @@ public class MDMang { * @throws MDException upon parsing error */ public MDDataType demangleType(boolean errorOnRemainingChars) throws MDException { - if (mangled == null) { - throw new MDException("MDMang: Mangled string is null."); - } initState(); - pushContext(); - if (peek() != '.') { - throw new MDException("MDMang: Mangled string is not that of a type."); - } - increment(); - MDDataType mdDataType = MDDataTypeParser.parseDataType(this, false); + MDDataType mdDataType = MDDataTypeParser.determineAndParseDataType(this, false); item = mdDataType; - if (mdDataType != null) { - mdDataType.parse(); - } int numCharsRemaining = getNumCharsRemaining(); - popContext(); if (errorOnRemainingChars && (numCharsRemaining > 0)) { throw new MDException( "MDMang: characters remain after demangling: " + numCharsRemaining + "."); diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/MDDataTypeParser.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/MDDataTypeParser.java index 4c733ff47a..7da58d9f17 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/MDDataTypeParser.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/MDDataTypeParser.java @@ -17,6 +17,7 @@ package mdemangler.datatype; import mdemangler.MDException; import mdemangler.MDMang; +import mdemangler.MDMang.ProcessingMode; import mdemangler.datatype.complex.*; import mdemangler.datatype.extended.*; import mdemangler.datatype.modifier.*; @@ -31,6 +32,52 @@ import mdemangler.object.MDObjectCPP; * by calling the appropriate parser at the appropriate place in the code. */ public class MDDataTypeParser { + /** + * This method is only to be used by MDMang itself for the highest level type parsing where + * there is not already a multi-retry. This method checks for the '.' starting character, + * determines the type by calling the {@link #parseDataType(MDMang, boolean)}, parses the type, + * and does the multi-mode retry if there is an exception on the first pass as + * MDMangObjectParser does for generic mangled objects + * @param dmang - the MDMang driver + * @param isHighest - boolean indicating whether something else modifies or names the data + * type to be parsed, which impacts when certain overloaded CV modifiers can be applied. + * @return - a type derived from MDDataType + * @throws MDException on parsing error + */ + public static MDDataType determineAndParseDataType(MDMang dmang, boolean isHighest) + throws MDException { + + MDDataType dt = null; + if (dmang.peek() != '.') { + throw new MDException("MDMang: Mangled string is not that of a type."); + } + + dmang.setProcessingMode(ProcessingMode.DEFAULT_STANDARD); + try { + dmang.pushContext(); + dmang.increment(); // skip the '.' + dt = parseDataType(dmang, isHighest); + dt.parse(); + dmang.popContext(); + } + catch (MDException e1) { + dmang.resetState(); + dmang.setProcessingMode(ProcessingMode.LLVM); + try { + dmang.pushContext(); + dmang.increment(); // skip the '.' + dt = parseDataType(dmang, isHighest); + dt.parse(); + dmang.popContext(); + } + catch (MDException e2) { + throw new MDException( + "Reason1: " + e1.getMessage().trim() + "; Reason2: " + e2.getMessage().trim()); + } + } + return dt; + } + /** * This method parses all data types. Specifically, it parses void, data indirect types, * function indirect types, and all types parsed by parsePrimaryDataType(). diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java index 346c045fab..b592b64cbf 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java @@ -14992,6 +14992,19 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + // Note the suffix seems like an already or partially demangled name. Note that name0 + // seems like a plain tag (no closing '@'), there is a regular namespace delimiter "::", + // the suffix "3@" is almost like a backreference tag with the '@' closing the full + // qualified name... except... we've seen numbers that are beyond the backref range as + // here, but also have seen numbers like 18. + @Ignore + public void testMangledTypeWithNamespaceSuffix() throws Exception { + mangled = ".?AT@name0::3@"; + msTruth = ""; + mdTruth = msTruth; + demangleAndTest(); + } + //===================== @Test diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java index 0de4b04adf..c3c5fcf105 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java @@ -80,4 +80,18 @@ public class MDMangExtraTest extends AbstractGenericTest { assertEquals("k::j::i", qualifications.get(2).toString()); } + // Need to test the demangleType() method to make sure it does the retry with LLVM mode + @Test + public void testDemangleTypeWithRetry() throws Exception { + // Test string taken from MDMangBaseTest + String mangled = ".?AW4name0@?name1@name2@@YAX_N@Z@"; + String truth = "enum `void __cdecl name2::name1(bool)'::name0"; + + MDMangGhidra demangler = new MDMangGhidra(); + MDParsableItem item = demangler.demangleType(mangled, true); // note demangleType() + + String demangled = item.toString(); + assertEquals(truth, demangled); + } + }