GP-4626 - MDMang updates for suffix on mangled type names plus other fixes

This commit is contained in:
ghizard 2024-06-03 18:03:40 -04:00
parent 2a83263d73
commit ead1cc5b37
14 changed files with 626 additions and 44 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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(<reason>): 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<String> 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;
}
}

View File

@ -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<MDParsableItem> 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<String> 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<? extends MDMang> 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);
}
}
}
/******************************************************************************/
/******************************************************************************/

View File

@ -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.
*

View File

@ -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;
}

View File

@ -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

View File

@ -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, ")");

View File

@ -96,6 +96,16 @@ public class MDQualification extends MDParsableItem implements Iterable<MDQualif
@Override
protected void parseInternal() throws MDException {
// We currently have a check to see if the index has moved (loc) to know to abort the
// processing of this loop. We could have also done a loop check on the '`' character
// from an LLVM suffix on mangled "type" name that looks like:
// "`fedcba98" ('`' character followed by exactly 8 (zero padded) hex digits
// TODO: need to determine what they are and where they should get processed... but
// could be part of end of MDQualification, MDQualifiedName, MDClassType,
// MDQuestionModifierType, or at same level as complete mangled name. For now, we
// have put the processing in MDQuestionModifierType, but this will take future study
// to determine what it is and to what object it belongs.
// We have a similar issue with dot-separated symbols... needs more study.
while ((dmang.peek() != MDMang.DONE) && (dmang.peek() != '@')) {
int loc = dmang.getIndex();
MDQualifier qual = new MDQualifier(dmang);
@ -111,6 +121,21 @@ public class MDQualification extends MDParsableItem implements Iterable<MDQualif
if (dmang.peek() == '@') {
dmang.increment(); // Skip past @.
}
// // For future debugging to try to figure out where to process the '`' suffix
// if (dmang.peek() == '`') {
// Exception e = new Exception();
// StackTraceElement[] trace = e.getStackTrace();
// StringBuilder builder = new StringBuilder();
// for (StackTraceElement t : trace) {
// String s = t.toString();
// builder.append(s);
// builder.append('\n');
// if (s.contains("MDMang.demangle(")) {
// System.out.println(builder.toString());
// break;
// }
// }
// }
}
@Override

View File

@ -48,6 +48,10 @@ public class MDQualifier extends MDParsableItem {
return nameNested;
}
public MDReusableName getAnonymousName() {
return nameAnonymous;
}
@Override
public void insert(StringBuilder builder) {
// Only one of these will hit.

View File

@ -70,7 +70,24 @@ public class MDMangObjectParser {
public static MDParsableItem parseDefaultStandard(MDMang dmang) throws MDException {
MDParsableItem item;
dmang.setProcessingMode(ProcessingMode.DEFAULT_STANDARD);
if (dmang.peek() == '?') {
// We believe this should adequately distinguish between a mangled "type" name vs. a
// dot-separated mangled "symbol" name.
// => 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 <b><code>MDMang</code></b> 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());

View File

@ -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) {

View File

@ -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<struct _name1,struct _name2 const >";
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<struct__name1,struct__name2_const_>";
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<class name4::name6<class name4::name7<struct name4::name8<struct name4::name3::name2::name9> > >,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<class name4::name6<class name4::name7<struct name4::name8<struct name4::name3::name2::name9> > >,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<class `public: unsigned __int64 __cdecl `anonymous namespace'::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>(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<class `public: unsigned __int64 __cdecl `anonymous namespace'::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>(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 {

View File

@ -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);