diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java index aa7f1118da..8b145ca3ac 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java @@ -255,7 +255,8 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { Address typeInfoVftableAddress = null; try { - typeInfoVftableAddress = RttiUtil.findTypeInfoVftableAddress(program, TaskMonitor.DUMMY); + typeInfoVftableAddress = + RttiUtil.findTypeInfoVftableAddress(program, TaskMonitor.DUMMY); } catch (CancelledException e) { throw new AssertException(e); @@ -356,10 +357,11 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { throw new UndefinedValueException( "No vf table pointer is defined for this TypeDescriptor model."); } - + Address vfTableAddress; // component 0 is either vf table pointer or hash value. - vfTableAddress = EHDataTypeUtilities.getAddress(getDataType(), VF_TABLE_OR_HASH_ORDINAL, getMemBuffer()); + vfTableAddress = + EHDataTypeUtilities.getAddress(getDataType(), VF_TABLE_OR_HASH_ORDINAL, getMemBuffer()); return (vfTableAddress != null && vfTableAddress.getOffset() != 0) ? vfTableAddress : null; } @@ -624,17 +626,11 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { private static MDComplexType getMDComplexType(Program program, String mangledString) { MDMangGhidra demangler = new MDMangGhidra(); try { - MDParsableItem parsableItem = demangler.demangle(mangledString, true); - if (!(parsableItem instanceof MDDataType)) { - // Not an MDDataType as expected. - return null; - } - MDDataType mangledDt = (MDDataType) parsableItem; - if (mangledDt instanceof MDModifierType) { - MDModifierType modifierType = (MDModifierType) mangledDt; + MDDataType mangledDt = demangler.demangleType(mangledString, true); + if (mangledDt instanceof MDModifierType modifierType) { MDType refType = modifierType.getReferencedType(); - if (refType instanceof MDComplexType) { - return (MDComplexType) refType; + if (refType instanceof MDComplexType complexType) { + return complexType; } } return null; // Not an MDComplexType diff --git a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java index 431bce0319..2e879d17b1 100644 --- a/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java +++ b/Ghidra/Features/MicrosoftDemangler/src/main/java/ghidra/app/util/demangler/microsoft/MicrosoftDemangler.java @@ -71,13 +71,12 @@ public class MicrosoftDemangler implements Demangler { MDMangGhidra demangler = new MDMangGhidra(); try { - demangler.demangle(mangled, demangleOnlyKnownPatterns); + demangler.demangle(mangled, true, demangleOnlyKnownPatterns); DemangledObject object = demangler.getObject(); return object; } catch (MDException e) { - DemangledException de = - new DemangledException("Unable to demangle symbol: " + mangled); + DemangledException de = new DemangledException("Unable to demangle symbol: " + mangled); de.initCause(e); throw de; } diff --git a/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java b/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java new file mode 100644 index 0000000000..a283beb0c1 --- /dev/null +++ b/Ghidra/Features/MicrosoftDmang/developer_scripts/MDMangDeveloperGenericizeMangledNamesScript.java @@ -0,0 +1,140 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Genericize mangled names from a list. +// Input is a file name with a name on each line. Output is a file with the corresponding output +// on each line. +// If a name cannot be demangled, it will not be genericized. The line for that name will +// be output as follows: !Failed(): inputName +// +//@category Demangler + +import java.io.*; +import java.util.List; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; + +import docking.widgets.values.GValuesMap; +import ghidra.app.script.GhidraScript; +import ghidra.features.base.values.GhidraValuesMap; +import ghidra.util.MessageType; +import ghidra.util.StatusListener; +import mdemangler.MDException; +import mdemangler.MDMangGenericize; +import utilities.util.FileUtilities; + +public class MDMangDeveloperGenericizeMangledNamesScript extends GhidraScript { + + private static final String TITLE = "Genericize Mangled Names"; + private static final String INPUT_PROMPT = "Choose an input file"; + private static final String OUTPUT_PROMPT = "Choose an output file"; + + private static boolean validateInputFile(GValuesMap valueMap, StatusListener status) { + File file = valueMap.getFile(INPUT_PROMPT); + if (file == null) { + status.setStatusText("Input file must be selected.", MessageType.ERROR); + return false; + } + if (!file.exists()) { + status.setStatusText(file.getAbsolutePath() + " is not a valid file.", + MessageType.ERROR); + return false; + } + return true; + } + + private static boolean validateOutputFile(GValuesMap valueMap, StatusListener status) { + File fileIn = valueMap.getFile(INPUT_PROMPT); + File fileOut = valueMap.getFile(OUTPUT_PROMPT); + String fileNameIn = fileIn.getAbsolutePath(); + String fileNameOut = fileOut.getAbsolutePath(); + if (fileNameOut.equals(fileNameIn)) { + status.setStatusText("Output file cannot be same as input file '" + fileNameOut + "').", + MessageType.ERROR); + return false; + } + return true; + } + + @Override + protected void run() throws Exception { + + GhidraValuesMap values = new GhidraValuesMap(); + + values.defineFile(INPUT_PROMPT, null); + values.setValidator((valueMap, status) -> { + return validateInputFile(valueMap, status); + }); + values = askValues(TITLE, null, values); + File inputFile = values.getFile(INPUT_PROMPT); + String inputFileName = inputFile.getAbsolutePath(); + + // creating a default output and asking again, to include output file query + String outputFileName = FilenameUtils.removeExtension(inputFileName) + ".gen." + + FilenameUtils.getExtension(inputFileName); + values.defineFile(OUTPUT_PROMPT, new File(outputFileName)); + values.setValidator((valueMap, status) -> { + return validateInputFile(valueMap, status) && validateOutputFile(valueMap, status); + }); + values = askValues(TITLE, null, values); + inputFile = values.getFile(INPUT_PROMPT); // might have changed + inputFileName = inputFile.getAbsolutePath(); // might have changed + File outputFile = values.getFile(OUTPUT_PROMPT); + + if (outputFile.exists()) { + if (!askYesNo("Confirm Overwrite", "Overwrite file: " + outputFile.getName())) { + println("Operation canceled"); + return; + } + } + + FileWriter fileWriter = new FileWriter(outputFile); + try (BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) { + String message = "Processing " + inputFileName; + monitor.setMessage(message); + println(message); + List lines = FileUtilities.getLines(inputFile); + for (String name : lines) { + monitor.checkCancelled(); + String output = getProcessedName(name); + bufferedWriter.append(output); + bufferedWriter.append("\n"); + } + message = "Results located in: " + outputFile.getAbsolutePath(); + monitor.setMessage(message); + println(message); + } + } + + private String getProcessedName(String name) { + if (StringUtils.containsWhitespace(name)) { + return getError(name, "contains white space"); + } + MDMangGenericize demangler = new MDMangGenericize(); + try { + demangler.demangle(name, false); + } + catch (MDException e) { + return getError(name, e.getMessage()); + } + return demangler.getGenericSymbol(); + } + + private String getError(String name, String reason) { + return "!Failed(" + reason + "): " + name; + } + +} diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java new file mode 100644 index 0000000000..12452fec20 --- /dev/null +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDDotSeparatedItem.java @@ -0,0 +1,92 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mdemangler; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +/** + * This class represents a higher level than the highest level object that maps to a Microsoft + * mangled symbol... this might be temporary until we can take time to study much of the facets + * of dotted names in much more detail. For now, we will put each dot-separated component into + * a separate part for processing. + * Some places where we have seen dotted symbols: + * -Symbols from load time, where the dots represent namespace delimitation + * -includes CLI binaries, from CliTableMethodDef: "?A0xfedcba98.blah" + * -LLVM has flags or attributes; e.g., ".weak." prefix on a mangled datatype name; also had + * suffix of ".default.__xmm@blahblahblahblahblahblahblahblah" + */ +public class MDDotSeparatedItem extends MDParsableItem { + private List subItems = new ArrayList<>(); + private boolean firstIsDot = false; + + public MDDotSeparatedItem(MDMang dmang) { + super(dmang); + } + + @Override + protected void parseInternal() throws MDException { + // check first character + String whole = dmang.getMangledSymbol(); + int start = dmang.getIndex(); // better be zero... but we are not testing it + // We know that a beginning dot is found for mangled "type" names, but perhaps this could + // be found for starts of flags as well. + if (start != 0) { + return; + } + firstIsDot = (dmang.peek() == '.'); // might need this + List dotStrings = Arrays.asList(whole.split("\\.")); + + for (String sub : dotStrings) { + // I don't want to use reflection, but doing so for now as we investigate. + // The overall MDMang model revamping will take time, but is when this fix will + // likely occur. + MDParsableItem subItem = null; + try { + Constructor ctor = dmang.getClass().getDeclaredConstructor(); + MDMang subDmang = ctor.newInstance(); + subItem = subDmang.demangle(sub, false); + } + // might want to handle these separately for now... later can possibly group all + // together + catch (NoSuchMethodException e) { + e.printStackTrace(); + } + catch (SecurityException e) { + e.printStackTrace(); + } + catch (InstantiationException e) { + e.printStackTrace(); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + catch (IllegalArgumentException e) { + e.printStackTrace(); + } + catch (InvocationTargetException e) { + e.printStackTrace(); + } + subItems.add(subItem); + } + + } + +} + +/******************************************************************************/ +/******************************************************************************/ diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java index 38a7bcdae0..db26447a9b 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMang.java @@ -21,6 +21,7 @@ import java.util.List; import mdemangler.MDContext.MDContextType; import mdemangler.datatype.MDDataType; +import mdemangler.datatype.MDDataTypeParser; import mdemangler.datatype.modifier.MDCVMod; import mdemangler.naming.MDFragmentName; import mdemangler.naming.MDQualification; @@ -116,6 +117,57 @@ public class MDMang { return item; } + /** + * Demangles the mangled "type" name and returns a parsed MDDataType + * + * @param mangledIn the mangled "type" string to be demangled + * @param errorOnRemainingChars + * boolean flag indicating whether remaining characters causes an + * error + * @return the parsed MDDataType + * @throws MDException upon parsing error + */ + public MDDataType demangleType(String mangledIn, boolean errorOnRemainingChars) + throws MDException { + if (mangledIn == null || mangledIn.isEmpty()) { + throw new MDException("Invalid mangled symbol."); + } + setMangledSymbol(mangledIn); + return demangleType(errorOnRemainingChars); + } + + /** + * Demangles the mangled "type" name already stored and returns a parsed MDDataType + * + * @param errorOnRemainingChars + * boolean flag indicating whether remaining characters causes an + * error + * @return the parsed MDDataType + * @throws MDException upon parsing error + */ + public MDDataType demangleType(boolean errorOnRemainingChars) throws MDException { + if (mangled == null) { + throw new MDException("MDMang: Mangled string is null."); + } + pushContext(); + if (peek() != '.') { + throw new MDException("MDMang: Mangled string is not that of a type."); + } + increment(); + MDDataType mdDataType = MDDataTypeParser.parseDataType(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 + "."); + } + return mdDataType; + } + /** * Sets the mangled string to be demangled. * @@ -204,6 +256,14 @@ public class MDMang { iter.setIndex(index); } + /** + * Returns true if there are no more characters to iterate + * @return {@code true} if done + */ + public boolean done() { + return peek() == DONE; + } + /** * Returns the next character without incrementing the current index. * diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java index e9fc686053..58f728644a 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java @@ -51,9 +51,8 @@ public class MDMangGhidra extends MDMang { return dataTypeResult; } - @Override - public MDParsableItem demangle(String mangledArg, boolean demangleOnlyKnownPatterns) - throws MDException { + public MDParsableItem demangle(String mangledArg, boolean errorOnRemainingChars, + boolean demangleOnlyKnownPatterns) throws MDException { // TODO: Could possibly just ignore "demangleOnlyKnownpatterns" if (demangleOnlyKnownPatterns) { if (!(mangledArg.startsWith("?") || mangledArg.startsWith(".") || @@ -64,6 +63,13 @@ public class MDMangGhidra extends MDMang { } } + return demangle(mangledArg, errorOnRemainingChars); + } + + @Override + public MDParsableItem demangle(String mangledArg, boolean errorOnRemainingChars) + throws MDException { + this.mangledSource = mangledArg; MDParsableItem returnedItem = super.demangle(mangledArg, true); @@ -74,6 +80,20 @@ public class MDMangGhidra extends MDMang { return returnedItem; } + @Override + public MDDataType demangleType(String mangledArg, boolean errorOnRemainingChars) + throws MDException { + + this.mangledSource = mangledArg; + + MDDataType returnedType = super.demangleType(mangledArg, errorOnRemainingChars); + + this.demangledSource = returnedType.toString(); + + dataTypeResult = processDataType(null, returnedType); + return returnedType; + } + public DemangledType processNamespace(MDQualifiedName qualifiedName) { return processNamespace(qualifiedName.getQualification()); } @@ -108,24 +128,24 @@ public class MDMangGhidra extends MDMang { if (item instanceof MDObjectReserved) { objectResult = processObjectReserved((MDObjectReserved) item); } - else if (item instanceof MDObjectCodeView) { - objectResult = processObjectCPP((MDObjectCPP) item); - objectResult.setSpecialPrefix(((MDObjectCodeView) item).getPrefix()); + else if (item instanceof MDObjectCodeView codeView) { + objectResult = processObjectCPP(codeView); + objectResult.setSpecialPrefix(codeView.getPrefix()); } - else if (item instanceof MDObjectCPP) { // Base class of MDObjectBracket/MDObjectCodeView. - objectResult = processObjectCPP((MDObjectCPP) item); + else if (item instanceof MDObjectCPP objCpp) { // Base class of MDObjectBracket/MDObjectCodeView. + objectResult = processObjectCPP(objCpp); } - else if (item instanceof MDObjectC) { - objectResult = processObjectC((MDObjectC) item); + else if (item instanceof MDObjectC objC) { + objectResult = processObjectC(objC); } - else if (item instanceof MDDataType) { + else if (item instanceof MDDataType dataType) { // TODO: how do we fix this? DemangledDataType extends DemangledType, but not // DemangleObject... - dataTypeResult = processDataType(null, (MDDataType) item); + dataTypeResult = processDataType(null, dataType); // object = getDemangledDataType(); } - else if (item instanceof MDTemplateNameAndArguments) { - objectResult = processTemplate((MDTemplateNameAndArguments) item); + else if (item instanceof MDTemplateNameAndArguments templateNameAndArgs) { + objectResult = processTemplate(templateNameAndArgs); } return objectResult; } diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/modifier/MDQuestionModifierType.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/modifier/MDQuestionModifierType.java index 5bba64b58c..346a405509 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/modifier/MDQuestionModifierType.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/datatype/modifier/MDQuestionModifierType.java @@ -26,6 +26,8 @@ import mdemangler.datatype.MDDataTypeParser; */ public class MDQuestionModifierType extends MDModifierType { + private String suffix; + public MDQuestionModifierType(MDMang dmang) { super(dmang); cvMod.setQuestionType(); @@ -34,6 +36,46 @@ public class MDQuestionModifierType extends MDModifierType { @Override protected void parseInternal() throws MDException { super.parseInternal(); + // Let's try to absorb the '`' suffix here (from certain llvm mangled type names) of + // form "`fedcba98" where there are 8 zero-padded hex digits after a back tick. + parseSuffix(); + } + + /** + * Returns a possible suffix + * @return the suffix or {@code null} + */ + public String getSuffix() { + return suffix; + } + + private void parseSuffix() { + if (dmang.peek() == '`') { + // TODO: when we know what this is and where it belongs, we should consider making + // it another MDParsableItem that includes the push/pop on processing so we + // can see the capture of the suffix in MDParseInfo. + int baseLoc = dmang.getIndex(); + StringBuilder builder = new StringBuilder(); + builder.append(dmang.getAndIncrement()); + for (int i = 0; i < 8; i++) { + if (dmang.done()) { + // failed to get all characters for the suffix; set the index back and leave + dmang.setIndex(baseLoc); + return; + } + char c = dmang.getAndIncrement(); + if (!isHexDigit(c)) { + dmang.setIndex(baseLoc); + return; + } + builder.append(c); + } + suffix = builder.toString(); + } + } + + private static boolean isHexDigit(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } @Override diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/functiontype/MDThrowAttribute.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/functiontype/MDThrowAttribute.java index 1368f2caba..9352fefe4f 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/functiontype/MDThrowAttribute.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/functiontype/MDThrowAttribute.java @@ -23,6 +23,8 @@ import mdemangler.*; */ public class MDThrowAttribute extends MDParsableItem { private MDArgumentsList argsList; + // TODO: consider whether the following two can be consolidated down to one variable + private boolean isNoExcept = false; private boolean hasThrow = true; public MDThrowAttribute(MDMang dmang) { @@ -32,7 +34,12 @@ public class MDThrowAttribute extends MDParsableItem { @Override protected void parseInternal() throws MDException { - if (dmang.peek() == 'Z') { + if (dmang.peek() == '_' && dmang.peek(1) == 'E') { + dmang.increment(2); + isNoExcept = true; + hasThrow = false; + } + else if (dmang.peek() == 'Z') { dmang.increment(); hasThrow = false; } @@ -43,7 +50,10 @@ public class MDThrowAttribute extends MDParsableItem { @Override public void insert(StringBuilder builder) { - if (hasThrow) { + if (isNoExcept) { + dmang.appendString(builder, "noexcept"); + } + else if (hasThrow) { dmang.appendString(builder, "throw("); argsList.insert(builder); dmang.appendString(builder, ")"); diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDQualification.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDQualification.java index 6ee54a7d5e..398d546204 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDQualification.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/naming/MDQualification.java @@ -96,6 +96,16 @@ public class MDQualification extends MDParsableItem implements Iterable This does not work on three of the tests that begin with "?.cctor" +// if (dmang.getMangledSymbol().substring(1).contains(".")) { +// // Dot-separated name +// // TODO: we need to deal with these +// // item = new MDDotSeparatedItem(dmang); // for research purposes; might not keep +// item = null; +// } + // Moved the following up for now because we failed (above) to properly distinguish + // dot-separated names for three tests. Ultimately, it might be better to not have + // a separate MDDotSeparatedItem, but to be able to set some attributes on a main item. + if (dmang.peek() == '.' && !dmang.getMangledSymbol().substring(1).contains(".")) { + dmang.increment(); + item = MDDataTypeParser.parseDataType(dmang, false); + } + else if (dmang.peek() == '?') { if (dmang.peek(1) == '@') { item = new MDObjectCodeView(dmang); } @@ -85,10 +102,11 @@ public class MDMangObjectParser { ((dmang.peek(1) == '_') || ((dmang.peek(1) >= 'A') && (dmang.peek(1) <= 'Z')))) { item = parseObjectReserved(dmang); } - else if (dmang.peek() == '.') { - dmang.increment(); - item = MDDataTypeParser.parseDataType(dmang, false); - } +// else if (dmang.peek() == '.' ) { +// // Dot-separated will not enter here, as they are distinguished earlier. +// dmang.increment(); +// item = MDDataTypeParser.parseDataType(dmang, false); +// } else { item = new MDObjectC(dmang); } @@ -150,7 +168,7 @@ public class MDMangObjectParser { * @throws MDException Upon MDMang parsing issues that cause us to fail * processing. */ - public static MDParsableItem parseObjectReserved(MDMang dmang) { + private static MDParsableItem parseObjectReserved(MDMang dmang) { MDParsableItem item; if (dmang.positionStartsWith("__TI")) { dmang.increment("__TI".length()); diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java index 04fab3f7c4..09959ff954 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDGhidraTestConfiguration.java @@ -49,8 +49,8 @@ public class MDGhidraTestConfiguration extends MDBaseTestConfiguration { protected MDParsableItem doDemangleSymbol(MDMang mdmIn, String mangledIn) throws Exception { MDParsableItem returnItem; try { - //Set true in operational mode. - returnItem = ((MDMangGhidra) mdmIn).demangle(mangledIn, false); // "false" is different + // For first boolean: set true in operational mode. + returnItem = ((MDMangGhidra) mdmIn).demangle(mangledIn, false, false); demangledObject = ((MDMangGhidra) mdmIn).getObject(); } catch (MDException e) { diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java index de963a96c2..ab40a2c595 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangBaseTest.java @@ -3706,6 +3706,14 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + @Test + public void testFunctionNoExcept() throws Exception { + mangled = "?fnii@@YAHH@_E"; + msTruth = "int __cdecl fnii(int) noexcept"; + mdTruth = msTruth; + demangleAndTest(); + } + @Test public void testFunctionThrow_a() throws Exception { mangled = "?fnii@@YAHH@@"; @@ -14741,6 +14749,174 @@ public class MDMangBaseTest extends AbstractGenericTest { demangleAndTest(); } + //===================== + + @Test + public void testUnionType() throws Exception { + mangled = ".?ATmyUnion@@"; + msTruth = "union myUnion"; + mdTruth = msTruth; + demangleAndTest(); + } + + @Test + public void testStructType() throws Exception { + mangled = ".?AUmyStruct@@"; + msTruth = "struct myStruct"; + mdTruth = msTruth; + demangleAndTest(); + } + + @Test + public void testClassType() throws Exception { + mangled = ".?AVmyClass@@"; + msTruth = "class myClass"; + mdTruth = msTruth; + demangleAndTest(); + } + + @Test + public void testEnumType() throws Exception { + mangled = ".?AW4myEnum@@"; + msTruth = "enum myEnum"; + mdTruth = msTruth; + demangleAndTest(); + } + + //===================== + // The following are tests to support working on dot-separated symbol names. They are + // ignored for now, as we have nothing to support the processing at this time + + // MSFT CLI symbol from loader. Has anonymous namespace (mangled), a dot delimiter to + // represent namespace delimiter, and then a "normal" name (which in this case is a + // demangled name with spaces). + @Ignore + public void testDotSeparatedSymbolCliOrig() throws Exception { + mangled = "?A0xfedcba98.name0"; + msTruth = ""; + mdTruth = ""; + demangleAndTest(); + } + + //Same MSFT CLI symbol from loader, but where SymbolUtilities was used to replace spaces + // with underscores. We need to determine that we can process either way... then need to + // make decision on whether symbols from load should get processed by demangler before + // SymbolUtilities gets a hold of them... this is generally contrary to what we would want, + // but if we cannot lay down symbols with spaces, then we need to make sure we can still + // operate either way + @Ignore + public void testDotSeparatedSymbolCliWithUnderscores() throws Exception { + mangled = "?A0xfedcba98.name0"; + msTruth = ""; + mdTruth = ""; + demangleAndTest(); + } + + // Complete LLVM type symbol with weak prefix and associated suffix + @Ignore + public void testLLVMWeakPrefixDefaultXmmSuffix() throws Exception { + mangled = ".weak.??_7name0@@6B@.default.__xmm@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + //llTruth = ""; // fail + msTruth = ""; + mdTruth = "const name0::`vftable'"; + demangleAndTest(); + } + + // Not expected to be a true symbol, but want to understand partial processing with + // complete suffix + @Ignore + public void testVftableLLVMWeakPartial1() throws Exception { + mangled = "??_7name0@@6B@.default.__xmm@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + //llTruth = "const name0::`vftable'"; + msTruth = ""; + mdTruth = msTruth; + demangleAndTest(); + } + + // Not expected to be a true symbol, but want to understand partial processing with + // partial suffix + @Ignore + public void testVftableLLVMWeakPartial2() throws Exception { + mangled = "??_7name0@@6B@.default"; + //llTruth = "const name0::`vftable'"; + msTruth = ""; + mdTruth = msTruth; + demangleAndTest(); + } + + @Ignore + public void testVftable() throws Exception { + mangled = "??_7name0@@6B@"; + msTruth = "const name0::`vftable'"; + mdTruth = msTruth; + demangleAndTest(); + } + + //===================== + + @Test + public void testLLVM177639() throws Exception { + mangled = + ".?AVname0@?3??name1@name2@?Aname3@@QEAA_KQEBXQEAX_KQ6A_K2PEAX3P6A_KPEAD23@_E@Z@Z@`fedcba98"; + //llTruth = ""; // fails + msTruth = ""; + mdTruth = + "class `public: unsigned __int64 __cdecl `anonymous namespace'::name2::name1(void const * __ptr64 const,void * __ptr64 const,unsigned __int64,unsigned __int64 (__cdecl*const)(unsigned __int64,void * __ptr64,void * __ptr64,unsigned __int64 (__cdecl*)(char * __ptr64,unsigned __int64,void * __ptr64) noexcept)) __ptr64'::`4'::name0"; + demangleAndTest(); + } + + //===================== + + @Test + public void testLLVMType1WithSuffix() throws Exception { + mangled = + ".?AVname0@?1??name1@name2@name3@name4@@AEAA?AU?$name5@V?$name6@V?$name7@U?$name8@Uname9@name2@name3@name4@@@name4@@@name4@@@name4@@E@4@_K0@Z@`fedcba98"; + //llTruth = ""; // fails + msTruth = ""; + // We don't do anything with the suffix at this time... TODO: consider options as + // things are figured out + mdTruth = + "class `private: struct name4::name5 > >,unsigned char> __cdecl name4::name3::name2::name1(unsigned __int64,unsigned __int64) __ptr64'::`2'::name0"; + demangleAndTest(); + } + + @Test + public void testLLVMType1MinusSuffix() throws Exception { + mangled = + ".?AVname0@?1??name1@name2@name3@name4@@AEAA?AU?$name5@V?$name6@V?$name7@U?$name8@Uname9@name2@name3@name4@@@name4@@@name4@@@name4@@E@4@_K0@Z@"; + //llTruth = ""; + msTruth = ""; + mdTruth = + "class `private: struct name4::name5 > >,unsigned char> __cdecl name4::name3::name2::name1(unsigned __int64,unsigned __int64) __ptr64'::`2'::name0"; + demangleAndTest(); + } + + //===================== + + @Test + public void testLLVMType2WithSuffix() throws Exception { + mangled = + ".?AVname0@?1???$name1@Vname0@?3??name2@name3@?Aname4@@QEAA_KQEBXQEAX_KQ6A_K2PEAX3P6A_KPEAD23@_E@Z@Z@@?Aname4@@YA_KQ6A_K_KPEAX1P6A_KPEAD01@_E@Z_KQEAXV0?3??name2@name3@1@QEAA_KQEBX604@Z@@Z@`fedcba98"; + msTruth = ""; + // We don't do anything with the suffix at this time... TODO: consider options as + // things are figured out + mdTruth = + "class `unsigned __int64 __cdecl `anonymous namespace'::name1(unsigned __int64 (__cdecl*const)(unsigned __int64,void * __ptr64,void * __ptr64,unsigned __int64 (__cdecl*)(char * __ptr64,unsigned __int64,void * __ptr64) noexcept),unsigned __int64,void * __ptr64 const,class `public: unsigned __int64 __cdecl Aname4::name3::name2(void const * __ptr64 const,void * __ptr64 const,unsigned __int64,unsigned __int64 (__cdecl*const)(unsigned __int64,void * __ptr64,void * __ptr64,unsigned __int64 (__cdecl*)(char * __ptr64,unsigned __int64,void * __ptr64) noexcept)) __ptr64'::`4'::name0)'::`2'::name0"; + demangleAndTest(); + } + + @Test + public void testLLVMType2MinusSuffix() throws Exception { + mangled = + ".?AVname0@?1???$name1@Vname0@?3??name2@name3@?Aname4@@QEAA_KQEBXQEAX_KQ6A_K2PEAX3P6A_KPEAD23@_E@Z@Z@@?Aname4@@YA_KQ6A_K_KPEAX1P6A_KPEAD01@_E@Z_KQEAXV0?3??name2@name3@1@QEAA_KQEBX604@Z@@Z@"; + msTruth = ""; + mdTruth = + "class `unsigned __int64 __cdecl `anonymous namespace'::name1(unsigned __int64 (__cdecl*const)(unsigned __int64,void * __ptr64,void * __ptr64,unsigned __int64 (__cdecl*)(char * __ptr64,unsigned __int64,void * __ptr64) noexcept),unsigned __int64,void * __ptr64 const,class `public: unsigned __int64 __cdecl Aname4::name3::name2(void const * __ptr64 const,void * __ptr64 const,unsigned __int64,unsigned __int64 (__cdecl*const)(unsigned __int64,void * __ptr64,void * __ptr64,unsigned __int64 (__cdecl*)(char * __ptr64,unsigned __int64,void * __ptr64) noexcept)) __ptr64'::`4'::name0)'::`2'::name0"; + demangleAndTest(); + } + + //===================== + //TODO: ignore for now. @Ignore public void testFuzzyFit() throws Exception { diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java index 614dc5c440..0de4b04adf 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDMangExtraTest.java @@ -15,7 +15,7 @@ */ package mdemangler; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.util.List; @@ -46,7 +46,7 @@ public class MDMangExtraTest extends AbstractGenericTest { String functionNamespaceTruth = "public: static int __cdecl Foo2b::Bar3(void)"; MDMangGhidra demangler = new MDMangGhidra(); - MDParsableItem item = demangler.demangle(mangled, true); + MDParsableItem item = demangler.demangle(mangled, true, true); String demangled = item.toString(); assertEquals(wholeTruth, demangled); @@ -54,7 +54,7 @@ public class MDMangExtraTest extends AbstractGenericTest { String mangledFunctionNamespace = obj.getNamespace().getNamespace().getMangledString(); assertEquals(functionNamespaceMangledTruth, mangledFunctionNamespace); - item = demangler.demangle(mangledFunctionNamespace, true); + item = demangler.demangle(mangledFunctionNamespace, true, true); demangled = item.toString(); assertEquals(functionNamespaceTruth, demangled); } @@ -66,7 +66,7 @@ public class MDMangExtraTest extends AbstractGenericTest { String truth = "const b::a::`vftable'{for `e::d::c's `h::g::f's `k::j::i'}"; MDMangGhidra demangler = new MDMangGhidra(); - MDParsableItem item = demangler.demangle(mangled, true); + MDParsableItem item = demangler.demangle(mangled, true, true); String demangled = item.toString(); assertEquals(truth, demangled);