mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-09-20 09:31:47 +00:00
GP-4626 - MDMang updates for suffix on mangled type names plus other fixes
This commit is contained in:
parent
2a83263d73
commit
ead1cc5b37
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ")");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user