diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/AbstractDemangledFunctionDefinitionDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/AbstractDemangledFunctionDefinitionDataType.java new file mode 100644 index 0000000000..ffe54bdd7f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/AbstractDemangledFunctionDefinitionDataType.java @@ -0,0 +1,330 @@ +/* ### + * 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 ghidra.app.util.demangler; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.program.model.data.*; +import ghidra.program.model.symbol.Namespace; + +/** + * Parent base class for types that represent things that refer to functions + */ +public abstract class AbstractDemangledFunctionDefinitionDataType extends DemangledDataType { + + protected static final String DEFAULT_NAME_PREFIX = "FuncDef"; + protected static final String EMPTY_STRING = ""; + protected static int ID = 0; + protected DemangledDataType returnType; + protected String callingConvention;// __cdecl, __thiscall, etc. + protected List parameters = new ArrayList<>(); + protected String modifier;// namespace::, etc. + protected boolean isConstPointer; + + protected String parentName; + protected boolean isTrailingPointer64; + protected boolean isTrailingUnaligned; + protected boolean isTrailingRestrict; + + /** display parens in front of parameter list */ + protected boolean displayFunctionPointerParens = true; + + AbstractDemangledFunctionDefinitionDataType(String mangled, String originalDemangled) { + super(mangled, originalDemangled, DEFAULT_NAME_PREFIX + nextId()); + } + + private synchronized static int nextId() { + return ID++; + } + + /** + * Returns the string for this type of reference (e.g., * or &) + * @return the string + */ + abstract protected String getTypeString(); + + @Override + public String getSignature() { + return toSignature(null); + } + + /** + * Sets the return type + * @param returnType the return type + */ + public void setReturnType(DemangledDataType returnType) { + this.returnType = returnType; + } + + /** + * Returns the return type + * @return the return type + */ + public DemangledDataType getReturnType() { + return returnType; + } + + /** + * Sets the function calling convention. For example, "__cdecl" + * @param callingConvention the function calling convention + */ + public void setCallingConvention(String callingConvention) { + this.callingConvention = callingConvention; + } + + /** + * Returns the calling convention or null, if unspecified + * @return the calling convention or null, if unspecified + */ + public String getCallingConvention() { + return callingConvention; + } + + /** + * Sets the function __ modifier. For example, "namespace::". + * @param modifier the function modifier + */ + public void setModifier(String modifier) { + this.modifier = modifier; + } + + public boolean isConstPointer() { + return isConstPointer; + } + + public void setConstPointer() { + isConstPointer = true; + } + + public boolean isTrailingPointer64() { + return isTrailingPointer64; + } + + public void setTrailingPointer64() { + isTrailingPointer64 = true; + } + + public boolean isTrailingUnaligned() { + return isTrailingUnaligned; + } + + public void setTrailingUnaligned() { + isTrailingUnaligned = true; + } + + public boolean isTrailingRestrict() { + return isTrailingRestrict; + } + + public void setTrailingRestrict() { + isTrailingRestrict = true; + } + + public void setDisplayFunctionPointerParens(boolean b) { + this.displayFunctionPointerParens = b; + } + + /** + * Adds a parameters to the end of the parameter list for this demangled function + * @param parameter the new parameter to add + */ + public void addParameter(DemangledDataType parameter) { + parameters.add(parameter); + } + + /** + * Returns a list of the parameters for this demangled functions. + * @return a list of the parameters for this demangled functions + */ + public List getParameters() { + return new ArrayList<>(parameters); + } + + public String toSignature(String name) { + StringBuilder buffer = new StringBuilder(); + StringBuilder buffer1 = new StringBuilder(); + String s = getConventionPointerNameString(name); + if (s.contains(" ") || s.isEmpty()) { + // spaces--add parens + addFunctionPointerParens(buffer1, s); + } + else { // this allows the '__cdecl' in templates to not have parens + buffer1.append(s); + } + + buffer1.append('('); + for (int i = 0; i < parameters.size(); ++i) { + buffer1.append(parameters.get(i).getSignature()); + if (i < parameters.size() - 1) { + buffer1.append(','); + } + } + buffer1.append(')'); + + if (returnType instanceof DemangledFunctionPointer) { + DemangledFunctionPointer dfp = (DemangledFunctionPointer) returnType; + buffer.append(dfp.toSignature(buffer1.toString())).append(SPACE); + } + else if (returnType instanceof DemangledFunctionReference) { + DemangledFunctionReference dfr = (DemangledFunctionReference) returnType; + buffer.append(dfr.toSignature(buffer1.toString())).append(SPACE); + } + else if (returnType instanceof DemangledFunctionIndirect) { + DemangledFunctionIndirect dfi = (DemangledFunctionIndirect) returnType; + buffer.append(dfi.toSignature(buffer1.toString())).append(SPACE); + } + else { + buffer.append(returnType.getSignature()).append(SPACE); + buffer.append(buffer1); + } + + if (isConst()) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(CONST); + } + + if (isVolatile()) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(VOLATILE); + } + + if (isTrailingUnaligned) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(UNALIGNED); + } + + if (isTrailingPointer64) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(PTR64); + } + + if (isTrailingRestrict) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(RESTRICT); + } + + return buffer.toString(); + } + + protected String getConventionPointerNameString(String name) { + StringBuilder buffer = new StringBuilder(); + buffer.append(callingConvention == null ? EMPTY_STRING : callingConvention); + + int pointerLevels = getPointerLevels(); + if (pointerLevels > 0) { + if (callingConvention != null) { + buffer.append(SPACE); + } + + addParentName(buffer); + + for (int i = 0; i < pointerLevels; ++i) { + buffer.append(getTypeString()); + } + } + + if ((modifier != null) && (modifier.length() != 0)) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(modifier); + } + + if (isConstPointer) { + buffer.append(CONST); + } + + if (isPointer64()) { + if (buffer.length() > 2) { + buffer.append(SPACE); + } + buffer.append(PTR64); + } + + if (name != null) { + if ((buffer.length() > 2) && (buffer.charAt(buffer.length() - 1) != SPACE)) { + buffer.append(SPACE); + } + buffer.append(name); + } + + return buffer.toString(); + } + + protected void addFunctionPointerParens(StringBuilder buffer, String s) { + if (!displayFunctionPointerParens) { + return; + } + + buffer.append('(').append(s).append(')'); + } + + protected void addParentName(StringBuilder buffer) { + if (parentName == null) { + return; + } + + if (parentName.startsWith(DEFAULT_NAME_PREFIX)) { + return; + } + + if (buffer.length() > 2) { + char lastChar = buffer.charAt(buffer.length() - 1); + if (SPACE != lastChar) { + buffer.append(SPACE); + } + } + buffer.append(parentName).append(Namespace.DELIMITER); + } + + @Override + public DataType getDataType(DataTypeManager dataTypeManager) { + + FunctionDefinitionDataType fddt = new FunctionDefinitionDataType(getName()); + + if (returnType != null) { + fddt.setReturnType(returnType.getDataType(dataTypeManager)); + } + + if (parameters.size() != 1 || + !(parameters.get(0).getDataType(dataTypeManager) instanceof VoidDataType)) { + ParameterDefinition[] params = new ParameterDefinition[parameters.size()]; + for (int i = 0; i < parameters.size(); ++i) { + params[i] = new ParameterDefinitionImpl(null, + parameters.get(i).getDataType(dataTypeManager), null); + } + fddt.setArguments(params); + } + + DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName()); + if (dt == null || !(dt instanceof FunctionDefinitionDataType)) { + dt = fddt; + } + + return new PointerDataType(dt, dataTypeManager); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java index ff549e9f7b..212f401ab7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/Demangled.java @@ -19,7 +19,7 @@ package ghidra.app.util.demangler; * A unifying top-level interface for all {@link DemangledObject}s and {@link DemangledType}s * *

This class and its children have many overlapping concepts that we wish to refine at a - * future date. Below is a listing of know uses: + * future date. Below is a listing of known uses: * * * @@ -38,7 +38,7 @@ package ghidra.app.util.demangler; * {@link #getDemangledName()} * * * * @@ -46,9 +46,12 @@ package ghidra.app.util.demangler; * {@link #getNamespaceName()} * * * * @@ -56,7 +59,13 @@ package ghidra.app.util.demangler; * {@link #getNamespaceString()} * * * * @@ -73,7 +82,8 @@ package ghidra.app.util.demangler; * {@link #getOriginalDemangled()} * * * *
MethodDescription - * The unmodified name that was set upon this object. + * The unmodified name that was set upon this object. *
- * The name of this object when it is used as a namespace name. This usually has + * The 'safe' name of this object when it is used as a namespace name. This usually has * parameter and template information. Further, some characters within templates and * function signatures are replaced, such as spaces and namespace separators. + *

+ * Given this full demangled string: {@code Foo::Bar::Baz}, this method will return + * {@code Baz}. *

- * Similar to {@link #getNamespaceName()}, but contains all parent namespaces as well. + * This returns the unmodified name of this item, along with any unmodified parent + * namespace names, all separated by a namespace delimiter. Unlike + * {@link #getNamespaceName()}, the spaces and internal namespace tokens will not be + * replaced. + *

+ * Given this full demangled string: {@code Foo::Bar::Baz}, this method will return + * {@code Foo::Bar::Baz}. *

- * The original unmodified demangled string. + * The original unmodified demangled string. This is the full demangled string returned + * from the demangling service. *
@@ -86,24 +96,12 @@ public interface Demangled { */ public String getMangledString(); - /** - * Sets the original mangled string - * @param mangled the mangled string - */ - public void setMangledString(String mangled); - /** * Returns the original demangled string returned by the demangling service * @return the original demangled string */ public String getOriginalDemangled(); - /** - * Sets the original demangles string returned by the demangling service - * @param originalDemangled the original demangled string - */ - public void setOriginalDemangled(String originalDemangled); - /** * Returns the demangled name of this object. * NOTE: unsupported symbol characters, like whitespace, will be converted to an underscore. @@ -147,7 +145,7 @@ public interface Demangled { public String getNamespaceString(); /** - * Returns a this object's namespace name without the full-qualified parent path. The + * Returns this object's namespace name without the fully-qualified parent path. The * value returned here may have had some special characters replaced, such as ' ' replaced * with '_' and '::' replaced with '--'. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledAddressTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledAddressTable.java index c4af8d467b..e129d26cbd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledAddressTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledAddressTable.java @@ -29,8 +29,20 @@ public class DemangledAddressTable extends DemangledObject { private boolean calculateLength; private int length; - public DemangledAddressTable(String name, boolean calculateLength) { + /** + * Constructor + * + * @param mangled the source mangled string + * @param originalDemangled the original demangled string + * @param name the name of the address table + * @param calculateLength true if the length of this address table should be calculdated at + * analysis time + */ + public DemangledAddressTable(String mangled, String originalDemangled, String name, + boolean calculateLength) { + super(mangled, originalDemangled); setName(name); + this.calculateLength = calculateLength; } /** @@ -110,7 +122,7 @@ public class DemangledAddressTable extends DemangledObject { /** * Perform a best guess at the length of an address table assuming that * another label (or end of block) can be used to identify the end. - * @param program + * @param program the program * @param address start of address table * @return maximum length of table or -1 if address does not reside * within an initialized memory block diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java index 6b9d067b34..86cd6c4f9c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java @@ -58,7 +58,7 @@ public class DemangledDataType extends DemangledType { public final static String WCHAR_T = "wchar_t"; public final static String SHORT = "short"; public final static String INT = "int"; - public final static String INT0_T = "int0_t";//TODO + public final static String INT0_T = "int0_t"; public final static String LONG = "long"; public final static String LONG_LONG = "long long"; public final static String FLOAT = "float"; @@ -67,8 +67,8 @@ public class DemangledDataType extends DemangledType { public final static String INT16 = "__int16"; public final static String INT32 = "__int32"; public final static String INT64 = "__int64"; - public final static String INT128 = "__int128";//TODO - public final static String FLOAT128 = "__float128";//TODO + public final static String INT128 = "__int128"; + public final static String FLOAT128 = "__float128"; public final static String LONG_DOUBLE = "long double"; public final static String PTR64 = "__ptr64"; public final static String STRING = "string"; @@ -76,6 +76,11 @@ public class DemangledDataType extends DemangledType { public static final String UNALIGNED = "__unaligned"; public static final String RESTRICT = "__restrict"; + private static final String UNSIGNED_CHAR = "unsigned char"; + private static final String UNSIGNED_SHORT = "unsigned short"; + private static final String UNSIGNED_INT = "unsigned int"; + private static final String UNSIGNED_LONG = "unsigned long"; + public final static String[] PRIMITIVES = { VOID, BOOL, CHAR, WCHAR_T, SHORT, INT, INT0_T, LONG, LONG_LONG, FLOAT, DOUBLE, INT128, FLOAT128, LONG_DOUBLE, }; @@ -100,54 +105,13 @@ public class DemangledDataType extends DemangledType { private boolean isCoclass; private boolean isCointerface; - /** - * Constructs a new demangled datatype. - * @param name the name of the datatype - */ - public DemangledDataType(String name) { - super(name); - } - - public DemangledDataType copy() { - DemangledDataType copy = new DemangledDataType(getName()); - copy(this, copy); - return copy; - } - - protected void copy(DemangledDataType source, DemangledDataType destination) { - destination.arrayDimensions = source.arrayDimensions; - destination.isClass = source.isClass; - destination.isComplex = source.isComplex; - destination.isEnum = source.isEnum; - destination.isPointer64 = source.isPointer64; - destination.isReference = source.isReference; - destination.isSigned = source.isSigned; - destination.isStruct = source.isStruct; - destination.isTemplate = source.isTemplate; - destination.isUnion = source.isUnion; - destination.isUnsigned = source.isUnsigned; - destination.isVarArgs = source.isVarArgs; - destination.pointerLevels = source.pointerLevels; - - destination.isUnaligned = source.isUnaligned(); - destination.isRestrict = source.isRestrict(); - destination.basedName = source.getBasedName(); - destination.memberScope = source.getMemberScope(); - - destination.setNamespace(source.getNamespace()); - destination.setTemplate(source.getTemplate()); - destination.isCoclass = source.isCoclass; - destination.isCointerface = source.isCointerface; - - if (source.isConst()) { - destination.setConst(); - } + public DemangledDataType(String mangled, String originaDemangled, String name) { + super(mangled, originaDemangled, name); } /** - * Converts this demangled datatype into the corresponding Ghidra datatype. - * @param dataTypeManager the data type manager to be searched and whose data organization - * should be used + * Converts this demangled datatype into the corresponding Ghidra datatype + * @param dataTypeManager the manager to search and whose data organization should be used * @return the Ghidra datatype corresponding to the demangled datatype */ public DataType getDataType(DataTypeManager dataTypeManager) { @@ -163,11 +127,6 @@ public class DemangledDataType extends DemangledType { } if (dt == null) { - - // If custom type, look for it first - // TODO: this find method could be subject to name mismatch, although - // presence of namespace could help this if it existing and contained within - // an appropriate namespace category dt = findDataType(dataTypeManager, namespace, name); DataType baseType = dt; @@ -188,25 +147,23 @@ public class DemangledDataType extends DemangledType { } else if (isEnum()) { if (baseType == null || !(baseType instanceof Enum)) { - // TODO: Can't tell how big an enum is, - // Just use the size of a pointer - // 20170522: Modified following code to allow "some" sizing from MSFT. - if ((enumType == null) || "int".equals(enumType) || - "unsigned int".equals(enumType)) { + + if (enumType == null || INT.equals(enumType) || UNSIGNED_INT.equals(enumType)) { + // Can't tell how big an enum is, just use the size of a pointer dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, dataTypeManager.getDataOrganization().getIntegerSize()); } - else if ("char".equals(enumType) || "unsigned char".equals(enumType)) { + else if (CHAR.equals(enumType) || UNSIGNED_CHAR.equals(enumType)) { dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, dataTypeManager.getDataOrganization().getCharSize()); } - else if ("short".equals(enumType) || "unsigned short".equals(enumType)) { + else if (SHORT.equals(enumType) || UNSIGNED_SHORT.equals(enumType)) { dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, dataTypeManager.getDataOrganization().getShortSize()); } - else if ("long".equals(enumType) || "unsigned long".equals(enumType)) { + else if (LONG.equals(enumType) || UNSIGNED_LONG.equals(enumType)) { dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, dataTypeManager.getDataOrganization().getLongSize()); } @@ -216,7 +173,7 @@ public class DemangledDataType extends DemangledType { } } } - else if (isClass() || name.equals(STRING)) {//TODO - class datatypes?? + else if (isClass() || name.equals(STRING)) { if (baseType == null || !(baseType instanceof Structure)) { // try creating empty structures for unknown types instead. dt = createPlaceHolderStructure(name, getNamespace()); @@ -251,7 +208,7 @@ public class DemangledDataType extends DemangledType { private DataType getBuiltInType(DataTypeManager dataTypeManager) { DataType dt = null; - String name = getName(); + String name = getDemangledName(); if (BOOL.equals(name)) { dt = BooleanDataType.dataType; } @@ -304,6 +261,9 @@ public class DemangledDataType extends DemangledType { else if (FLOAT.equals(name)) { dt = FloatDataType.dataType; } + else if (FLOAT128.equals(name)) { + dt = new TypedefDataType(FLOAT128, Float16DataType.dataType); + } else if (DOUBLE.equals(name)) { dt = DoubleDataType.dataType; } @@ -350,6 +310,16 @@ public class DemangledDataType extends DemangledType { AbstractIntegerDataType.getSignedDataType(8, dataTypeManager)); } } + else if (INT128.equals(name)) { + if (isUnsigned()) { + dt = new TypedefDataType("__uint128", + AbstractIntegerDataType.getUnsignedDataType(16, dataTypeManager)); + } + else { + dt = new TypedefDataType(INT128, + AbstractIntegerDataType.getSignedDataType(16, dataTypeManager)); + } + } else if (UNDEFINED.equals(name)) { dt = DataType.DEFAULT; } @@ -629,6 +599,7 @@ public class DemangledDataType extends DemangledType { return false; } + @Override public String getSignature() { StringBuilder buffer = new StringBuilder(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java index 222a8cbcb3..ec4e6b8b5a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java @@ -65,11 +65,8 @@ public class DemangledFunction extends DemangledObject { private boolean isTypeCast; private String throwAttribute; - /** - * Constructs a new demangled function. - * @param name the name of the function - */ - public DemangledFunction(String name) { + public DemangledFunction(String mangled, String originalDemangled, String name) { + super(mangled, originalDemangled); setName(name); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionIndirect.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionIndirect.java index e4810dd37e..88a782f433 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionIndirect.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionIndirect.java @@ -15,11 +15,6 @@ */ package ghidra.app.util.demangler; -import java.util.ArrayList; -import java.util.List; - -import ghidra.program.model.data.*; - /** * A class to represent a demangled function indirect. A function indirect is * similar to a function pointer or a function reference except that it does @@ -27,332 +22,14 @@ import ghidra.program.model.data.*; * is still an indirect definition (not a regular function definition). The * function indirect is prevalent in the Microsoft model, if not other models. */ -public class DemangledFunctionIndirect extends DemangledDataType { +public class DemangledFunctionIndirect extends AbstractDemangledFunctionDefinitionDataType { - private static final String DEFAULT_NAME_PREFIX = "FuncDef"; - private static final String NAMESPACE_DELIMITER = "::"; - private static final String EMPTY_STRING = ""; - private static int ID = 0; - private DemangledDataType returnType; - protected String callingConvention;// __cdecl, __thiscall, etc. - private List parameters = new ArrayList<>(); - protected String modifier;// namespace::, etc. - protected boolean isConstPointer; - - private String parentName; - private boolean isTrailingPointer64; - private boolean isTrailingUnaligned; - private boolean isTrailingRestrict; - - /** display parens in front of parameter list */ - private boolean displayFunctionPointerParens = true; - - /** - * Constructs a new demangled function definition. - */ - public DemangledFunctionIndirect() { - super("FuncDef" + nextID()); - } - - private synchronized static int nextID() { - return ID++; - } - - /** - * Returns the return type. - * @return the return type - */ - public DemangledDataType getReturnType() { - return returnType; - } - - /** - * Sets the return type. - * @param returnType the return type - */ - public void setReturnType(DemangledDataType returnType) { - this.returnType = returnType; - } - - /** - * Returns the calling convention or null, if unspecified. - * @return the calling convention or null, if unspecified - */ - public String getCallingConvention() { - return callingConvention; - } - - /** - * Sets the function calling convention. For example, "__cdecl". - * @param callingConvention the function calling convention - */ - public void setCallingConvention(String callingConvention) { - this.callingConvention = callingConvention; - } - - /** - * Sets the function __ modifier. For example, "namespace::". - * @param modifier the function modifier - */ - public void setModifier(String modifier) { - this.modifier = modifier; - } - - public boolean isConstPointer() { - return isConstPointer; - } - - public void setConstPointer() { - isConstPointer = true; - } - - public boolean isTrailingPointer64() { - return isTrailingPointer64; - } - - public void setTrailingPointer64() { - isTrailingPointer64 = true; - } - - public boolean isTrailingUnaligned() { - return isTrailingUnaligned; - } - - public void setTrailingUnaligned() { - isTrailingUnaligned = true; - } - - public boolean isTrailingRestrict() { - return isTrailingRestrict; - } - - public void setTrailingRestrict() { - isTrailingRestrict = true; - } - - public void setDisplayFunctionPointerParens(boolean b) { - this.displayFunctionPointerParens = b; - } - - /** - * Adds a parameters to the end of the parameter list for this demangled function - * @param parameter the new parameter to add - */ - public void addParameter(DemangledDataType parameter) { - parameters.add(parameter); - } - - /** - * Returns a list of the parameters for this demangled functions - * @return a list of the parameters for this demangled functions - */ - public List getParameters() { - return new ArrayList<>(parameters); + public DemangledFunctionIndirect(String mangled, String originalDemangled) { + super(mangled, originalDemangled); } @Override - public DemangledDataType copy() { - DemangledFunctionIndirect copy = new DemangledFunctionIndirect(); - copy(this, copy); - return copy; - } - - @Override - protected void copy(DemangledDataType source, DemangledDataType destination) { - super.copy(source, destination); - if ((source instanceof DemangledFunctionIndirect) && - (destination instanceof DemangledFunctionIndirect)) { - DemangledFunctionIndirect copySource = (DemangledFunctionIndirect) source; - DemangledFunctionIndirect copyDestination = (DemangledFunctionIndirect) destination; - - copyDestination.returnType = copySource.returnType.copy(); - for (DemangledDataType parameter : copySource.parameters) { - copyDestination.parameters.add(parameter.copy()); - } - - copyDestination.callingConvention = copySource.callingConvention; - } - } - - @Override - public String getSignature() { - return toSignature(null); - } - - public String toSignature(String name) { - StringBuilder buffer = new StringBuilder(); - StringBuilder buffer1 = new StringBuilder(); - String s = getConventionPointerNameString(name); - if (s.contains(" ") || s.isEmpty()) { - // spaces--add parens - addFunctionPointerParens(buffer1, s); - } - else { // this allows the '__cdecl' in templates to not have parens - buffer1.append(s); - } - - buffer1.append('('); - for (int i = 0; i < parameters.size(); ++i) { - buffer1.append(parameters.get(i).getSignature()); - if (i < parameters.size() - 1) { - buffer1.append(','); - } - } - buffer1.append(')'); - - if (returnType instanceof DemangledFunctionPointer) { - buffer.append( - ((DemangledFunctionPointer) returnType).toSignature(buffer1.toString())) - .append( - SPACE); - } - else if (returnType instanceof DemangledFunctionReference) { - buffer.append( - ((DemangledFunctionReference) returnType).toSignature(buffer1.toString())) - .append( - SPACE); - } - else if (returnType instanceof DemangledFunctionIndirect) { - buffer.append( - ((DemangledFunctionIndirect) returnType).toSignature(buffer1.toString())) - .append( - SPACE); - } - else { - buffer.append(returnType.getSignature()).append(SPACE); - buffer.append(buffer1); - } - - if (isConst()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(CONST); - } - - if (isVolatile()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(VOLATILE); - } - - if (isTrailingUnaligned) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(UNALIGNED); - } - - if (isTrailingPointer64) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(PTR64); - } - - if (isTrailingRestrict) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(RESTRICT); - } - - return buffer.toString(); - } - - private void addFunctionPointerParens(StringBuilder buffer, String s) { - if (!displayFunctionPointerParens) { - return; - } - - buffer.append('(').append(s).append(')'); - } - - private String getConventionPointerNameString(String name) { - StringBuilder buffer = new StringBuilder(); - buffer.append(callingConvention == null ? EMPTY_STRING : callingConvention); - - int pointerLevels = getPointerLevels(); - if (pointerLevels > 0) { -// if (callingConvention != null) { -// buffer.append(SPACE); -// } - - addParentName(buffer); - -// for (int i = 0; i < pointerLevels; ++i) { -// buffer.append('*'); -// } - } - - if ((modifier != null) && (modifier.length() != 0)) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(modifier); - } - - if (isConstPointer) { - buffer.append(CONST); - } - - if (isPointer64()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(PTR64); - } - - if (name != null) { - if ((buffer.length() > 2) && (buffer.charAt(buffer.length() - 1) != SPACE)) { - buffer.append(SPACE); - } - buffer.append(name); - } - - return buffer.toString(); - } - - private void addParentName(StringBuilder buffer) { - if (parentName == null) { - return; - } - - if (parentName.startsWith(DEFAULT_NAME_PREFIX)) { - return; - } - - if (buffer.length() > 2) { - char lastChar = buffer.charAt(buffer.length() - 1); - if (SPACE != lastChar) { - buffer.append(SPACE); - } - } - buffer.append(parentName).append(NAMESPACE_DELIMITER); - } - - @Override - public DataType getDataType(DataTypeManager dataTypeManager) { - - FunctionDefinitionDataType fddt = new FunctionDefinitionDataType(getName()); - fddt.setReturnType(returnType.getDataType(dataTypeManager)); - - if (parameters.size() != 1 || - !(parameters.get(0).getDataType(dataTypeManager) instanceof VoidDataType)) { - ParameterDefinition[] params = new ParameterDefinition[parameters.size()]; - for (int i = 0; i < parameters.size(); ++i) { - params[i] = new ParameterDefinitionImpl(null, - parameters.get(i).getDataType(dataTypeManager), null); - } - fddt.setArguments(params); - } - - DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName()); - if (dt == null || !(dt instanceof FunctionDefinitionDataType)) { - dt = fddt; - } - - return new PointerDataType(dt, dataTypeManager); + protected String getTypeString() { + return EMPTY_STRING; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionPointer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionPointer.java index 70f7a45245..f6519bef42 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionPointer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionPointer.java @@ -15,337 +15,17 @@ */ package ghidra.app.util.demangler; -import java.util.ArrayList; -import java.util.List; - -import ghidra.program.model.data.*; - /** - * A class to represent a demangled function pointer. + * A class to represent a demangled function pointer */ -public class DemangledFunctionPointer extends DemangledDataType { +public class DemangledFunctionPointer extends AbstractDemangledFunctionDefinitionDataType { - private static final String DEFAULT_NAME_PREFIX = "FuncDef"; - private static final Object NAMESPACE_DELIMITER = "::"; - private static final String EMPTY_STRING = ""; - private static int ID = 0; - private DemangledDataType returnType; - protected String callingConvention;// __cdecl, __thiscall, etc. - private List parameters = new ArrayList<>(); - protected String modifier;// namespace::, etc. - protected boolean isConstPointer; - - private String parentName; - private boolean isTrailingPointer64; - private boolean isTrailingUnaligned; - private boolean isTrailingRestrict; - - /** display parens in front of parameter list */ - private boolean displayFunctionPointerParens = true; - - /** - * Constructs a new demangled function pointer. - */ - public DemangledFunctionPointer() { - super("FuncDef" + nextID()); - } - - private synchronized static int nextID() { - return ID++; - } - - /** - * Returns the return type. - * @return the return type - */ - public DemangledDataType getReturnType() { - return returnType; - } - - /** - * Sets the return type. - * @param returnType the return type - */ - public void setReturnType(DemangledDataType returnType) { - this.returnType = returnType; - } - - /** - * Returns the calling convention or null, if unspecified. - * @return the calling convention or null, if unspecified - */ - public String getCallingConvention() { - return callingConvention; - } - - /** - * Sets the function calling convention. For example, "__cdecl". - * @param callingConvention the function calling convention - */ - public void setCallingConvention(String callingConvention) { - this.callingConvention = callingConvention; - } - - /** - * Sets the function __ modifier. For example, "namespace::". - * @param modifier the function modifier - */ - public void setModifier(String modifier) { - this.modifier = modifier; - } - - public boolean isConstPointer() { - return isConstPointer; - } - - public void setConstPointer() { - isConstPointer = true; - } - - public boolean isTrailingPointer64() { - return isTrailingPointer64; - } - - public void setTrailingPointer64() { - isTrailingPointer64 = true; - } - - public boolean isTrailingUnaligned() { - return isTrailingUnaligned; - } - - public void setTrailingUnaligned() { - isTrailingUnaligned = true; - } - - public boolean isTrailingRestrict() { - return isTrailingRestrict; - } - - public void setTrailingRestrict() { - isTrailingRestrict = true; - } - - public void setDisplayFunctionPointerParens(boolean b) { - this.displayFunctionPointerParens = b; - } - - /** - * Adds a parameters to the end of the parameter list for this demangled function - * @param parameter the new parameter to add - */ - public void addParameter(DemangledDataType parameter) { - parameters.add(parameter); - } - - /** - * Returns a list of the parameters for this demangled functions. - * @return a list of the parameters for this demangled functions - */ - public List getParameters() { - return new ArrayList<>(parameters); + public DemangledFunctionPointer(String mangled, String originalDemangled) { + super(mangled, originalDemangled); } @Override - public DemangledDataType copy() { - DemangledFunctionPointer copy = new DemangledFunctionPointer(); - copy(this, copy); - return copy; - } - - @Override - protected void copy(DemangledDataType source, DemangledDataType destination) { - super.copy(source, destination); - if ((source instanceof DemangledFunctionPointer) && - (destination instanceof DemangledFunctionPointer)) { - DemangledFunctionPointer copySource = (DemangledFunctionPointer) source; - DemangledFunctionPointer copyDestination = (DemangledFunctionPointer) destination; - - copyDestination.returnType = copySource.returnType.copy(); - for (DemangledDataType parameter : copySource.parameters) { - copyDestination.parameters.add(parameter.copy()); - } - - copyDestination.callingConvention = copySource.callingConvention; - } - } - - @Override - public String getSignature() { - return toSignature(null); - } - - public String toSignature(String name) { - StringBuilder buffer = new StringBuilder(); - StringBuilder buffer1 = new StringBuilder(); - String s = getConventionPointerNameString(name); - if (s.contains(" ") || s.isEmpty()) { - // spaces--add parens - addFunctionPointerParens(buffer1, s); - } - else { // this allows the '__cdecl' in templates to not have parens - buffer1.append(s); - } - - buffer1.append('('); - for (int i = 0; i < parameters.size(); ++i) { - buffer1.append(parameters.get(i).getSignature()); - if (i < parameters.size() - 1) { - buffer1.append(','); - } - } - buffer1.append(')'); - - if (returnType instanceof DemangledFunctionPointer) { - DemangledFunctionPointer dfp = (DemangledFunctionPointer) returnType; - buffer.append(dfp.toSignature(buffer1.toString())).append(SPACE); - } - else if (returnType instanceof DemangledFunctionReference) { - DemangledFunctionReference dfr = (DemangledFunctionReference) returnType; - buffer.append(dfr.toSignature(buffer1.toString())).append(SPACE); - } - else if (returnType instanceof DemangledFunctionIndirect) { - DemangledFunctionIndirect dfi = (DemangledFunctionIndirect) returnType; - buffer.append(dfi.toSignature(buffer1.toString())).append(SPACE); - } - else { - buffer.append(returnType.getSignature()).append(SPACE); - buffer.append(buffer1); - } - - if (isConst()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(CONST); - } - - if (isVolatile()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(VOLATILE); - } - - if (isTrailingUnaligned) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(UNALIGNED); - } - - if (isTrailingPointer64) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(PTR64); - } - - if (isTrailingRestrict) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(RESTRICT); - } - - return buffer.toString(); - } - - private void addFunctionPointerParens(StringBuilder buffer, String s) { - if (!displayFunctionPointerParens) { - return; - } - - buffer.append('(').append(s).append(')'); - } - - private String getConventionPointerNameString(String name) { - StringBuilder buffer = new StringBuilder(); - buffer.append(callingConvention == null ? EMPTY_STRING : callingConvention); - - int pointerLevels = getPointerLevels(); - if (pointerLevels > 0) { - if (callingConvention != null) { - buffer.append(SPACE); - } - - addParentName(buffer); - - for (int i = 0; i < pointerLevels; ++i) { - buffer.append('*'); - } - } - - if ((modifier != null) && (modifier.length() != 0)) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(modifier); - } - - if (isConstPointer) { - buffer.append(CONST); - } - - if (isPointer64()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(PTR64); - } - - if (name != null) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(name); - } - - return buffer.toString(); - } - - private void addParentName(StringBuilder buffer) { - if (parentName == null) { - return; - } - - if (parentName.startsWith(DEFAULT_NAME_PREFIX)) { - return; - } - - if (buffer.length() > 2) { - char lastChar = buffer.charAt(buffer.length() - 1); - if (SPACE != lastChar) { - buffer.append(SPACE); - } - } - buffer.append(parentName).append(NAMESPACE_DELIMITER); - } - - @Override - public DataType getDataType(DataTypeManager dataTypeManager) { - - FunctionDefinitionDataType fddt = new FunctionDefinitionDataType(getName()); - - if (returnType != null) { - fddt.setReturnType(returnType.getDataType(dataTypeManager)); - } - - if (parameters.size() != 1 || - !(parameters.get(0).getDataType(dataTypeManager) instanceof VoidDataType)) { - ParameterDefinition[] params = new ParameterDefinition[parameters.size()]; - for (int i = 0; i < parameters.size(); ++i) { - params[i] = new ParameterDefinitionImpl(null, - parameters.get(i).getDataType(dataTypeManager), null); - } - fddt.setArguments(params); - } - - DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName()); - if (dt == null || !(dt instanceof FunctionDefinitionDataType)) { - dt = fddt; - } - - return new PointerDataType(dt, dataTypeManager); + protected String getTypeString() { + return "*"; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionReference.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionReference.java index 98b43e8282..92b856dcb5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionReference.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunctionReference.java @@ -15,341 +15,17 @@ */ package ghidra.app.util.demangler; -import java.util.ArrayList; -import java.util.List; - -import ghidra.program.model.data.*; - /** - * A class to represent a demangled function reference. + * A class to represent a demangled function reference */ -public class DemangledFunctionReference extends DemangledDataType { +public class DemangledFunctionReference extends AbstractDemangledFunctionDefinitionDataType { - private static final String DEFAULT_NAME_PREFIX = "FuncDef"; - private static final Object NAMESPACE_DELIMITER = "::"; - private static final String EMPTY_STRING = ""; - private static int ID = 0; - private DemangledDataType returnType; - protected String callingConvention;// __cdecl, __thiscall, etc. - private List parameters = new ArrayList<>(); - protected String modifier;// namespace::, etc. - protected boolean isConstPointer; - - private String parentName; - private boolean isTrailingPointer64; - private boolean isTrailingUnaligned; - private boolean isTrailingRestrict; - - /** display parens in front of parameter list */ - private boolean displayFunctionPointerParens = true; - - /** - * Constructs a new demangled function reference. - */ - public DemangledFunctionReference() { - super("FuncDef" + nextID()); - } - - private synchronized static int nextID() { - return ID++; - } - - /** - * Returns the return type. - * @return the return type - */ - public DemangledDataType getReturnType() { - return returnType; - } - - /** - * Sets the return type. - * @param returnType the return type - */ - public void setReturnType(DemangledDataType returnType) { - this.returnType = returnType; - } - - /** - * Returns the calling convention or null, if unspecified. - * @return the calling convention or null, if unspecified - */ - public String getCallingConvention() { - return callingConvention; - } - - /** - * Sets the function calling convention. For example, "__cdecl". - * @param callingConvention the function calling convention - */ - public void setCallingConvention(String callingConvention) { - this.callingConvention = callingConvention; - } - - /** - * Sets the function __ modifier. For example, "namespace::". - * @param modifier the function modifier - */ - public void setModifier(String modifier) { - this.modifier = modifier; - } - - public boolean isConstPointer() { - return isConstPointer; - } - - public void setConstPointer() { - isConstPointer = true; - } - - public boolean isTrailingPointer64() { - return isTrailingPointer64; - } - - public void setTrailingPointer64() { - isTrailingPointer64 = true; - } - - public boolean isTrailingUnaligned() { - return isTrailingUnaligned; - } - - public void setTrailingUnaligned() { - isTrailingUnaligned = true; - } - - public boolean isTrailingRestrict() { - return isTrailingRestrict; - } - - public void setTrailingRestrict() { - isTrailingRestrict = true; - } - - public void setDisplayFunctionPointerParens(boolean b) { - this.displayFunctionPointerParens = b; - } - - /** - * Adds a parameters to the end of the parameter list for this demangled function - * @param parameter the new parameter to add - */ - public void addParameter(DemangledDataType parameter) { - parameters.add(parameter); - } - - /** - * Returns a list of the parameters for this demangled functions. - * @return a list of the parameters for this demangled functions - */ - public List getParameters() { - return new ArrayList<>(parameters); + public DemangledFunctionReference(String mangled, String originalDemangled) { + super(mangled, originalDemangled); } @Override - public DemangledDataType copy() { - DemangledFunctionReference copy = new DemangledFunctionReference(); - copy(this, copy); - return copy; - } - - @Override - protected void copy(DemangledDataType source, DemangledDataType destination) { - super.copy(source, destination); - if ((source instanceof DemangledFunctionReference) && - (destination instanceof DemangledFunctionReference)) { - DemangledFunctionReference copySource = (DemangledFunctionReference) source; - DemangledFunctionReference copyDestination = (DemangledFunctionReference) destination; - - copyDestination.returnType = copySource.returnType.copy(); - for (DemangledDataType parameter : copySource.parameters) { - copyDestination.parameters.add(parameter.copy()); - } - - copyDestination.callingConvention = copySource.callingConvention; - } - } - - @Override - public String getSignature() { - return toSignature(null); - } - - public String toSignature(String name) { - StringBuffer buffer = new StringBuffer(); - StringBuffer buffer1 = new StringBuffer(); - String s = getConventionPointerNameString(name); - if (s.contains(" ") || s.isEmpty()) { - // spaces--add parens - addFunctionPointerParens(buffer1, s); - } - else { // this allows the '__cdecl' in templates to not have parens - buffer1.append(s); - } - - buffer1.append('('); - for (int i = 0; i < parameters.size(); ++i) { - buffer1.append(parameters.get(i).getSignature()); - if (i < parameters.size() - 1) { - buffer1.append(','); - } - } - buffer1.append(')'); - - if (returnType instanceof DemangledFunctionPointer) { - buffer.append( - ((DemangledFunctionPointer) returnType).toSignature(buffer1.toString())) - .append( - SPACE); - } - else if (returnType instanceof DemangledFunctionReference) { - buffer.append( - ((DemangledFunctionReference) returnType).toSignature(buffer1.toString())) - .append( - SPACE); - } - else if (returnType instanceof DemangledFunctionIndirect) { - buffer.append( - ((DemangledFunctionIndirect) returnType).toSignature(buffer1.toString())) - .append( - SPACE); - } - else { - buffer.append(returnType.getSignature()).append(SPACE); - buffer.append(buffer1); - } - - if (isConst()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(CONST); - } - - if (isVolatile()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(VOLATILE); - } - - if (isTrailingUnaligned) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(UNALIGNED); - } - - if (isTrailingPointer64) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(PTR64); - } - - if (isTrailingRestrict) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(RESTRICT); - } - - return buffer.toString(); - } - - private void addFunctionPointerParens(StringBuffer buffer, String s) { - if (!displayFunctionPointerParens) { - return; - } - - buffer.append('(').append(s).append(')'); - } - - private String getConventionPointerNameString(String name) { - StringBuilder buffer = new StringBuilder(); - buffer.append(callingConvention == null ? EMPTY_STRING : callingConvention); - - int pointerLevels = getPointerLevels(); - if (pointerLevels > 0) { - if (callingConvention != null) { - buffer.append(SPACE); - } - - addParentName(buffer); - - buffer.append('&'); -// for (int i = 0; i < pointerLevels; ++i) { -// buffer.append('*'); -// } - } - - if ((modifier != null) && (modifier.length() != 0)) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(modifier); - } - - if (isConstPointer) { - buffer.append(CONST); - } - - if (isPointer64()) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(PTR64); - } - - if (name != null) { - if (buffer.length() > 2) { - buffer.append(SPACE); - } - buffer.append(name); - } - - return buffer.toString(); - } - - private void addParentName(StringBuilder buffer) { - if (parentName == null) { - return; - } - - if (parentName.startsWith(DEFAULT_NAME_PREFIX)) { - return; - } - - if (buffer.length() > 2) { - char lastChar = buffer.charAt(buffer.length() - 1); - if (SPACE != lastChar) { - buffer.append(SPACE); - } - } - buffer.append(parentName).append(NAMESPACE_DELIMITER); - } - - @Override - public DataType getDataType(DataTypeManager dataTypeManager) { - - FunctionDefinitionDataType fddt = new FunctionDefinitionDataType(getName()); - fddt.setReturnType(returnType.getDataType(dataTypeManager)); - - if (parameters.size() != 1 || - !(parameters.get(0).getDataType(dataTypeManager) instanceof VoidDataType)) { - ParameterDefinition[] params = new ParameterDefinition[parameters.size()]; - for (int i = 0; i < parameters.size(); ++i) { - params[i] = new ParameterDefinitionImpl(null, - parameters.get(i).getDataType(dataTypeManager), null); - } - fddt.setArguments(params); - } - - DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName()); - if (dt == null || !(dt instanceof FunctionDefinitionDataType)) { - dt = fddt; - } - - return new PointerDataType(dt, dataTypeManager); + protected String getTypeString() { + return "&"; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLambda.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLambda.java index 6e59758b36..6d8ec51af6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLambda.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLambda.java @@ -20,8 +20,8 @@ package ghidra.app.util.demangler; */ public class DemangledLambda extends DemangledFunction { - public DemangledLambda(String name) { - super(name); + public DemangledLambda(String mangled, String originalDemangled, String name) { + super(mangled, originalDemangled, name); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java index b2a185bd11..c4e47fb5a9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledObject.java @@ -41,8 +41,8 @@ public abstract class DemangledObject implements Demangled { protected static final String NAMESPACE_SEPARATOR = Namespace.DELIMITER; protected static final String EMPTY_STRING = ""; - protected String mangled; // original mangled string - protected String originalDemangled; + protected final String mangled; // original mangled string + protected final String originalDemangled; protected String specialPrefix; protected Demangled namespace; protected String visibility;//public, protected, etc. @@ -70,8 +70,9 @@ public abstract class DemangledObject implements Demangled { private String signature; - DemangledObject() { - // default + DemangledObject(String mangled, String originalDemangled) { + this.mangled = mangled; + this.originalDemangled = originalDemangled; } @Override @@ -180,21 +181,11 @@ public abstract class DemangledObject implements Demangled { } } - @Override - public void setMangledString(String mangled) { - this.mangled = mangled; - } - @Override public String getMangledString() { return mangled; } - @Override - public void setOriginalDemangled(String originalDemangled) { - this.originalDemangled = originalDemangled; - } - @Override public String getOriginalDemangled() { return originalDemangled; @@ -248,7 +239,7 @@ public abstract class DemangledObject implements Demangled { public abstract String getSignature(boolean format); @Override - public String getSignature() { + public final String getSignature() { return getSignature(false); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledString.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledString.java index ea104962fe..9d0399a7ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledString.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledString.java @@ -30,6 +30,8 @@ public class DemangledString extends DemangledObject { /** * Construct demangled string. + * @param mangled the source mangled string + * @param originalDemangled the original demangled string * @param name name associated with this object * @param string string text associated with this object or null. This is used to establish * label and plate comment if specified. If null, name will be used as symbol name. @@ -37,7 +39,9 @@ public class DemangledString extends DemangledObject { * assumes null terminated string. * @param unicode true if string is a Unicode string. */ - public DemangledString(String name, String string, int length, boolean unicode) { + public DemangledString(String mangled, String originalDemangled, String name, String string, + int length, boolean unicode) { + super(mangled, originalDemangled); setName(name); this.string = string; this.length = length; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java index 19deef799a..5c54b95fdd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledThunk.java @@ -34,7 +34,9 @@ public class DemangledThunk extends DemangledObject { private boolean covariantReturnThunk = false; - public DemangledThunk(DemangledFunction thunkedFunctionObject) { + public DemangledThunk(String mangled, String originalDemangled, + DemangledFunction thunkedFunctionObject) { + super(mangled, originalDemangled); this.thunkedFunctionObject = thunkedFunctionObject; this.namespace = thunkedFunctionObject.getNamespace(); setName(thunkedFunctionObject.getName()); @@ -115,14 +117,6 @@ public class DemangledThunk extends DemangledObject { return s != null; } - /** - * Create normal function where thunk resides - * @param prog program - * @param addr thunk function address - * @param doDisassembly - * @param monitor - * @return function - */ private Function createPreThunkFunction(Program prog, Address addr, boolean doDisassembly, TaskMonitor monitor) { @@ -182,7 +176,7 @@ public class DemangledThunk extends DemangledObject { } Symbol s = SymbolUtilities.getExpectedLabelOrFunctionSymbol(program, - thunkedFunctionObject.mangled, err -> Msg.warn(this, err)); + mangled, err -> Msg.warn(this, err)); if (s == null) { Address thunkedAddr = diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledType.java index 604ecfc391..6288ceab96 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledType.java @@ -24,16 +24,20 @@ import ghidra.program.model.symbol.Namespace; * to compose its internal state for namespace information, return types and parameters. */ public class DemangledType implements Demangled { - private String demangledName; - private String name; + protected String mangled; // the original mangled string + private String originalDemangled; + private String demangledName; + private String name; // 'safe' name + protected Demangled namespace; protected DemangledTemplate template; private boolean isConst; private boolean isVolatile; - private String originalDemangled; - public DemangledType(String name) { + public DemangledType(String mangled, String originaDemangled, String name) { + this.mangled = mangled; + this.originalDemangled = originaDemangled; setName(name); } @@ -57,21 +61,11 @@ public class DemangledType implements Demangled { } } - @Override - public void setOriginalDemangled(String originalDemangled) { - this.originalDemangled = originalDemangled; - } - @Override public String getOriginalDemangled() { return originalDemangled; } - @Override - public void setMangledString(String mangled) { - this.mangled = mangled; - } - @Override public String getMangledString() { return mangled; @@ -145,7 +139,7 @@ public class DemangledType implements Demangled { @Override public String getNamespaceName() { - return getName(false); + return name; } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledUnknown.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledUnknown.java index 6d6a1fa790..e630bb45ca 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledUnknown.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledUnknown.java @@ -26,10 +26,8 @@ import ghidra.program.model.symbol.SymbolUtilities; */ public class DemangledUnknown extends DemangledObject { - public DemangledUnknown() { - } - - public DemangledUnknown(String name) { + public DemangledUnknown(String mangled, String originalDemangled, String name) { + super(mangled, originalDemangled); setName(name); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledVariable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledVariable.java index a2daf2430d..c9a5092f52 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledVariable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledVariable.java @@ -36,7 +36,8 @@ import ghidra.util.task.TaskMonitor; public class DemangledVariable extends DemangledObject { private DemangledDataType datatype; - public DemangledVariable(String name) { + public DemangledVariable(String mangled, String originalDemangled, String name) { + super(mangled, originalDemangled); setName(name); } diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java index 2e1c9a91c7..ebc234d92f 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemangler.java @@ -113,9 +113,9 @@ public class GnuDemangler implements Demangler { } if (globalPrefix != null) { - // TODO: may need better naming convention for demangled function DemangledFunction dfunc = - new DemangledFunction(globalPrefix + demangledObject.getName()); + new DemangledFunction(originalMangled, demangled, + globalPrefix + demangledObject.getName()); dfunc.setNamespace(demangledObject.getNamespace()); demangledObject = dfunc; } @@ -123,14 +123,12 @@ public class GnuDemangler implements Demangler { demangledObject.setSignature(demangled); } - demangledObject.setMangledString(originalMangled); - if (isDwarf) { - DemangledAddressTable dat = new DemangledAddressTable((String) null, false); + DemangledAddressTable dat = + new DemangledAddressTable(originalMangled, demangled, (String) null, false); dat.setSpecialPrefix("DWARF Debug "); dat.setName(demangledObject.getName()); dat.setNamespace(demangledObject.getNamespace()); - dat.setMangledString(originalMangled); return dat; } diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java index 2acfad6d13..cc6fdb639d 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java @@ -27,6 +27,7 @@ import org.apache.commons.lang3.builder.ToStringStyle; import ghidra.app.util.SymbolPath; import ghidra.app.util.demangler.*; import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.symbol.Namespace; import ghidra.util.StringUtilities; public class GnuDemanglerParser { @@ -46,67 +47,85 @@ public class GnuDemanglerParser { TYPEINFO_FN_FOR, TYPEINFO_FOR); - private static final String NAMESPACE_DELIMITER = "::"; + private static final String OPERATOR = "operator"; + private static final String LAMBDA = "lambda"; + private static final String VAR_ARGS = "..."; + private static final String THUNK = "thunk"; + private static final String CONST = " const"; + private static final char NULL_CHAR = '\u0000'; - /** - *

-	 * Syntax: bob((Rect &, unsigned long))
+	/*
+	 * Sample:  bob((Rect &, unsigned long))
+	 *          bob(const(Rect &, bool))
 	 *
-	 * pattern: optional spaces followed by '()' with a capture group for the contents of the
-	 *          parens
-	 * note:    this pattern is used for matching the arguments string, in the above example it
-	 *          would be: (Rect &, unsigned long)
+	 * Pattern: name(([const] [params]))
+	 *
+	 * Parts: -optional spaces 
+	 * 			-optional (const)  (non-capture group)
+	 *          -followed by '()' with optinal parameter text (capture group 1)
 	 *          
-	 * Also matches: bob(const(Rect &, bool))
-	 * 
+ * Note: this pattern is used for matching the arguments string, in the above examples it + * would be: + * Rect &, unsigned long + * and + * Rect &, bool + * */ private static final Pattern UNNECESSARY_PARENS_PATTERN = - Pattern.compile("\\s*(const){0,1}\\((.*)\\)\\s*"); + Pattern.compile("\\s*(?:const){0,1}\\((.*)\\)\\s*"); - /** - *
-	 * Syntax: 	bob(short (&)[7])
+	/*
+	 * Sample: 	bob(short (&)[7])
 	 * 			bob(int const[8] (*) [12])
 	 *
-	 * 			   typename[optional '*'](*|&)[optional spaces][optional value]
+	 * Pattern: name[optional '*'](*|&)[optional spaces][optional value]
 	 *
-	 * pattern:
-	 * 				-a word
+	 * Parts:
+	 * 				-a word (capture group 1)
+	 *              -followed by an optional pointer '*'
 	 * 				-followed by a space
-	 * 				-*optional: any other text (e.g., const[8])
-	 * 				-followed by '()' that contain a '&' or a '*'
-	 * 				-followed by one or more '[]' with optional interior text
-	 * 
+ * -*optional: any other text (e.g., const[8]) (non-capture group) + * -followed by '()' that contain a '&' or a '*' (capture group 2) + * -followed by one or more '[]' with optional interior text (capture group 3) + * + * Group Samples: + * short (&)[7] + * 1 short + * 2 & + * 3 [7] + * + * CanRxItem (&) [2][64u] + * 1 CanRxItem + * 2 & + * 3 [2][64u] + * */ private static final Pattern ARRAY_POINTER_REFERENCE_PATTERN = - Pattern.compile("([\\w:]+)\\*?\\s(.*)\\(([&*])\\)\\s*((?:\\[.*?\\])+)"); + Pattern.compile("([\\w:]+)\\*?\\s(?:.*)\\(([&*])\\)\\s*((?:\\[.*?\\])+)"); - /** - *
-	 * Syntax: bob(short (&)[7])
+	/*
+	 * Sample:  bob(short (&)[7])
 	 *
-	 * 			   (*|&)[optional spaces][optional value]
+	 * Pattern: (*|&)[optional spaces][optional value]
 	 *
-	 * pattern: '()' that contain a '&' or a '*' followed by '[]' with optional text; a capture
-	 *          group for the contents of the parens
+	 * Parts:   
+	 * 			-'()' that contain a '&' or a '*' 
+	 *          -followed by '[]' with optional text
 	 * 
*/ private static final Pattern ARRAY_POINTER_REFERENCE_PIECE_PATTERN = - Pattern.compile("\\(([&*])\\)\\s*\\[.*?\\]"); + Pattern.compile("\\([&*]\\)\\s*\\[.*?\\]"); - /** - *
-	* Syntax: (unsigned)4294967295
+	/*
+	* Sample:  (unsigned)4294967295
 	*
-	* 			   (some text)[optional space]1 or more characters
+	* Pattern: (some text)[optional space]1 or more characters
 	*
-	* Regex:
-	*
-	* pattern:
+	* Parts:
 	* 			-parens containing text
-	* 			--the text can have "::" namespace separators (this is in a non-capturing group) and
-	*             must be followed by more text
-	*           --the text can have multiple words, such as (unsigned long)
+	* 				--the text can have "::" namespace separators (non-capturing group) and
+	*             	  must be followed by more text
+	*           	--the text can have multiple words, such as (unsigned long)
 	*           -optional space
 	*           -optional '-' character (a negative sign character)
 	* 			-followed by more text (with optional spaces)
@@ -115,91 +134,91 @@ public class GnuDemanglerParser {
 	private static final Pattern CAST_PATTERN =
 		Pattern.compile("\\((?:\\w+\\s)*\\w+(?:::\\w+)*\\)\\s*-{0,1}\\w+");
 
-	private static final String OPERATOR = "operator";
-
-	/**
-	 * 
-	 * Syntax: 
-	 *         Magick::operator<(Magick::Coordinate const&, Magick::Coordinate const&)
-	 * 		   std::basic_istream >& std::operator>> >(std::basic_istream >&, char&)
-	 * 
-	 * 		  [return_type] operator opeartor_character(s) (opeartor_params)
+	/*
+	 * Sample:  Magick::operator<(Magick::Coordinate const&, Magick::Coordinate const&)
+	 * 		    std::basic_istream >& std::operator>> >(std::basic_istream >&, char&)
+	 *          bool myContainer::operator<< (double)
+	 *         
+	 * Pattern: [return_type] operator operator_character(s) (opeartor_params) [trailing text]
 	 *
-	 * Regex:
-	 * 
-	 * pattern:
-	 * 			-maybe a return type
-	 * 			-operator
-	 * 			-operator character(s)
-	 *          -parameters
-	 * 
- * - * + * Parts: + * -optional a return type (capture group 1) + * -operator (capture group 2) + * -operator character(s) (capture group 3) + * -optional space + * -optional templates (capture group 4) + * -parameters (capture group 5) + * -trailing text (capture group 6) + * + * Note: this regex is generated from all known operator patterns and looks like: + * (.*operator(generated_text).*)\s*(\(.*\))(.*) */ private static final Pattern OVERLOAD_OPERATOR_PATTERN = createOverloadedOperatorPattern(); - /** - *
-	* Syntax: std::integral_constant::operator bool() const
-	*         Magick::Color::operator std::basic_string, std::allocator >() const
+	/*
+	* Sample:  std::integral_constant::operator bool() const
+	*          Magick::Color::operator std::basic_string, std::allocator >() const
 	*         
-	* pattern:
-	* 			-operator
+	* Pattern: operator type() [trailing text]
+	* 
+	* Parts:
+	* 			-operator (capture group 1)
 	* 			-space
-	*           -keyword for cast type
+	*           -keyword for cast type (capture group 2)
 	*           -optional keywords
 	*
-	* 
*/ private static final Pattern CONVERSION_OPERATOR_PATTERN = Pattern.compile("(.*" + OPERATOR + ") (.*)\\(\\).*"); - /** - *
-	* Syntax: operator new(unsigned long)
-	*         operator new(void*)
-	*         operator new[](void*)
+	/*
+	* Sample:  operator new(unsigned long)
+	*          operator new(void*)
+	*          operator new[](void*)
 	*
-	* pattern:
-	* 			-operator
+	* Pattern: operator new|delete[] ([parameters]) [trailing text]
+	*
+	* Parts:
+	* 			-operator (capture group 1)
 	* 			-space
-	*           -keyword 'new' or 'delete'
-	*           -optional array brackets
-	*           -optional parameters
+	*           -keyword 'new' or 'delete' (capture group 2)
+	*           -optional array brackets (capture group 3)
+	*           -optional parameters (capture group 4)
 	*
-	* 
*/ private static final Pattern NEW_DELETE_OPERATOR_PATTERN = Pattern.compile("(.*" + OPERATOR + ") (new|delete)(\\[\\])?\\((.*)\\).*"); - private static final String LAMBDA = "lambda"; - - /** + /* * Pattern for newer C++ lambda syntax: * - *
-	 *  {lambda(void const*, unsigned int)#1}
+	 * Sample:  {lambda(void const*, unsigned int)#1}
 	 * 
-	 *  1 - full text
-	 *  2 - params
-	 *  3 - trailing id
-	 *  
+ * Pattern: [optional text] brace lambda([parameters])#digits brace + * + * Parts: + * -full text without leading characters (capture group 1) + * -parameters of the lambda function (capture group 2) + * -trailing id (capture group 3) */ private static final Pattern LAMBDA_PATTERN = Pattern.compile(".*(\\{" + LAMBDA + "\\((.*)\\)(#\\d+)\\})"); - /** - * The c 'decltype' keyword pattern - */ - private static final Pattern DECLTYPE_RETURN_TYPE_PATTERN = - Pattern.compile("decltype \\(.*\\)"); - - // note: the '?' after the .* this is there to allow the trailing digits to match as many as - // possible - private static final Pattern ENDS_WITH_DIGITS_PATTERN = Pattern.compile("(.*?)\\d+"); - - /** + /* + * Sample: covariant return thunk to Foo::Bar::copy(Foo::CoolStructure*) const + * + * Pattern: text for|to text + * + * Parts: + * -required text (capture group 2) + * -a space + * -'for' or 'to' (capture group 3) + * -a space + * -optional text (capture group 4) + * + * Note: capture group 1 is the combination of groups 2 and 3 + * * Examples: * construction vtable for * vtable for @@ -211,12 +230,21 @@ public class GnuDemanglerParser { * non-virtual thunk to */ private static final Pattern DESCRIPTIVE_PREFIX_PATTERN = - Pattern.compile("((.+ )+(for|to) )(.*)"); + Pattern.compile("((.+ )+(for|to) )(.+)"); - private static final char NULL_CHAR = '\u0000'; - private static final String VAR_ARGS = "..."; - private static final String THUNK = "thunk"; - private static final String CONST_KEYWORD = " const"; + /** + * The c 'decltype' keyword pattern + */ + private static final Pattern DECLTYPE_RETURN_TYPE_PATTERN = + Pattern.compile("decltype \\(.*\\)"); + + /** + * Simple pattern to match any text that is trailed by digits + * + * note: the '?' after the .* this is there to allow the trailing digits to match as many as + * possible + */ + private static final Pattern ENDS_WITH_DIGITS_PATTERN = Pattern.compile("(.*?)\\d+"); private static Pattern createOverloadedOperatorPattern() { @@ -228,16 +256,27 @@ public class GnuDemanglerParser { "&", "|", ">>", "<<", "~", "^", "&&", "||", "!", "=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", ">>=", "<<=", - ",", "()" + ",", "()", + "\"\"" )); //@formatter:on CollectionUtils.transform(operators, Pattern::quote); String alternated = StringUtils.join(operators, "|"); - return Pattern.compile("(.*" + OPERATOR + "(" + alternated + ").*)\\s*(\\(.*\\))(.*)"); + String returnType = "(.* ){0,1}"; + String operatorTemplates = "(<.+>){0,1}"; + String operatorPrefix = + "(.*" + OPERATOR + "(" + alternated + ")\\s*" + operatorTemplates + ".*)\\s*"; + String parameters = "(\\(.*\\))"; + String trailing = "(.*)"; + + return Pattern.compile(returnType + operatorPrefix + parameters + trailing); } + private String mangledSource; + private String demangledSource; + /** * Parses the given demangled string and creates a {@link DemangledObject} * @@ -249,44 +288,45 @@ public class GnuDemanglerParser { public DemangledObject parse(String mangled, String demangled) throws DemanglerParseException { - OperatorHandler operatorHandler = getOperatorHandler(demangled); + this.mangledSource = mangled; + this.demangledSource = demangled; + + DemangledObjectBuilder builder = getSpecializedBuilder(demangled); + if (builder != null) { + return builder.build(); + } + + return parseFunctionOrVariable(demangled); + } + + private DemangledObjectBuilder getSpecializedBuilder(String demangled) { + + DemangledObjectBuilder operatorHandler = getOperatorHandler(demangled); if (operatorHandler != null) { - DemangledObject dobj = operatorHandler.build(); - dobj.setMangledString(mangled); - dobj.setOriginalDemangled(demangled); - return dobj; + return operatorHandler; } - SpecialPrefixHandler handler = getSpecialPrefixHandler(mangled, demangled); + DemangledObjectBuilder handler = getSpecialPrefixHandler(mangledSource, demangled); if (handler != null) { - String type = handler.getType(); - DemangledObject dobj = doParse(type); - DemangledObject specialPrefixDobj = handler.build(dobj); - specialPrefixDobj.setMangledString(mangled); - specialPrefixDobj.setOriginalDemangled(demangled); - return specialPrefixDobj; + return handler; } - DemangledObject dobj = doParse(demangled); - dobj.setMangledString(mangled); - dobj.setOriginalDemangled(demangled); - - return dobj; + return null; } private OperatorHandler getOperatorHandler(String demangled) { - OperatorHandler handler = new OverloadOperatorHandler(); + OperatorHandler handler = new OverloadOperatorHandler(demangled); if (handler.matches(demangled)) { return handler; } - handler = new ConversionOperatorHandler(); + handler = new ConversionOperatorHandler(demangled); if (handler.matches(demangled)) { return handler; } - handler = new NewOrDeleteOperatorHandler(); + handler = new NewOrDeleteOperatorHandler(demangled); if (handler.matches(demangled)) { return handler; } @@ -321,7 +361,7 @@ public class GnuDemanglerParser { return null; } - private DemangledObject doParse(String demangled) { + private DemangledObject parseFunctionOrVariable(String demangled) { ParameterLocator paramLocator = new ParameterLocator(demangled); if (!paramLocator.hasParameters()) { @@ -341,20 +381,21 @@ public class GnuDemanglerParser { int nameStart = Math.max(0, prefix.lastIndexOf(' ')); String name = prefix.substring(nameStart, prefix.length()).trim(); - DemangledFunction function = new DemangledFunction(null); + DemangledFunction function = new DemangledFunction(mangledSource, demangled, null); + String simpleName = name; LambdaName lambdaName = getLambdaName(demangled); if (lambdaName != null) { String uniqueName = lambdaName.getFullText(); String fullLambda = fixupInternalSeparators(uniqueName); simpleName = name.replace("{lambda", fullLambda); - function = new DemangledLambda(null); + function = new DemangledLambda(mangledSource, demangled, null); function.setSignature(lambdaName.getFullText()); } // For GNU, we cannot leave the return type as null, because the DemangleCmd will fill in // pointer to the class to accommodate windows demangling - function.setReturnType(new DemangledDataType("undefined")); + function.setReturnType(new DemangledDataType(mangledSource, demangled, "undefined")); for (DemangledDataType parameter : parameters) { function.addParameter(parameter); } @@ -367,7 +408,7 @@ public class GnuDemanglerParser { setReturnType(function, returnType); } - if (demangled.endsWith(CONST_KEYWORD)) { + if (demangled.endsWith(CONST)) { function.setConst(true); } @@ -420,16 +461,16 @@ public class GnuDemanglerParser { private DemangledObject parseItemInNamespace(String itemText) { - int pos = itemText.lastIndexOf(NAMESPACE_DELIMITER); + int pos = itemText.lastIndexOf(Namespace.DELIMITER); if (pos == -1) { throw new DemanglerParseException( "Expected the demangled string to contain a namespace"); } String parentText = itemText.substring(0, pos); - DemangledObject parent = doParse(parentText); + DemangledObject parent = parseFunctionOrVariable(parentText); String name = itemText.substring(pos + 2); - DemangledObject item = doParse(name); + DemangledObject item = parseFunctionOrVariable(name); item.setNamespace(parent); return item; } @@ -501,7 +542,7 @@ public class GnuDemanglerParser { // when demangling functions that have const at the end, such as bob(param1, param2) const; Matcher matcher = UNNECESSARY_PARENS_PATTERN.matcher(parameterString); if (matcher.matches()) { - parameterString = matcher.group(2); + parameterString = matcher.group(1); } if (StringUtils.isBlank(parameterString)) { @@ -629,8 +670,7 @@ public class GnuDemanglerParser { // special case: template parameter with a cast (just make the datatype // be the name of the template parameter, since it will just be a display // attribute for the templated type) - String value = castMatcher.group(0);// group 0 is the entire match - return new DemangledDataType(value); + return new DemangledDataType(mangledSource, demangledSource, fullDatatype); } DemangledDataType ddt = createTypeInNamespace(fullDatatype); @@ -660,7 +700,6 @@ public class GnuDemanglerParser { } if (ch == '<') {//start of template int contentStart = i + 1; - // int templateEnd = getTemplateEndIndex(datatype, contentStart); int templateEnd = findTemplateEnd(datatype, i); if (templateEnd == -1 || templateEnd > datatype.length()) { throw new DemanglerParseException("Did not find ending to template"); @@ -684,7 +723,7 @@ public class GnuDemanglerParser { if (arrayMatcher.matches()) { Demangled namespace = ddt.getNamespace(); String name = arrayMatcher.group(1);// group 0 is the entire string - ddt = parseArrayPointerOrReference(datatype, name); + ddt = parseArrayPointerOrReference(datatype, name, arrayMatcher); ddt.setNamespace(namespace); i = arrayMatcher.end(); } @@ -878,7 +917,7 @@ public class GnuDemanglerParser { } String datatypeName = names.get(names.size() - 1); - DemangledDataType ddt = new DemangledDataType(datatypeName); + DemangledDataType ddt = new DemangledDataType(mangledSource, demangledSource, datatypeName); ddt.setName(datatypeName); ddt.setNamespace(namespace); return ddt; @@ -907,8 +946,14 @@ public class GnuDemanglerParser { object.setNamespace(convertToNamespaces(names)); } - private DemangledTemplate parseTemplate(String templateStr) { - List parameters = parseParameters(templateStr); + private DemangledTemplate parseTemplate(String string) { + + String contents = string; + if (string.startsWith("<") && string.endsWith(">")) { + contents = string.substring(1, string.length() - 1); + } + + List parameters = parseParameters(contents); DemangledTemplate template = new DemangledTemplate(); for (DemangledDataType parameter : parameters) { template.addParameter(parameter); @@ -916,14 +961,13 @@ public class GnuDemanglerParser { return template; } - private DemangledDataType parseArrayPointerOrReference(String datatype, String name) { + private DemangledDataType parseArrayPointerOrReference(String datatype, String name, + Matcher matcher) { // int (*)[8] // char (&)[7] - DemangledDataType ddt = new DemangledDataType(name); - Matcher matcher = ARRAY_POINTER_REFERENCE_PATTERN.matcher(datatype); - matcher.find(); - String type = matcher.group(3); + DemangledDataType ddt = new DemangledDataType(mangledSource, demangledSource, name); + String type = matcher.group(2); if (type.equals("*")) { ddt.incrementPointerLevels(); } @@ -934,7 +978,7 @@ public class GnuDemanglerParser { throw new DemanglerParseException("Unexpected charater inside of parens: " + type); } - String arraySubscripts = matcher.group(4); + String arraySubscripts = matcher.group(3); int n = StringUtilities.countOccurrences(arraySubscripts, '['); ddt.setArray(n); @@ -976,7 +1020,7 @@ public class GnuDemanglerParser { List parameters = parseParameters(paramerterString); - DemangledFunctionPointer dfp = new DemangledFunctionPointer(); + DemangledFunctionPointer dfp = new DemangledFunctionPointer(mangledSource, demangledSource); dfp.setReturnType(parseDataType(returnType)); for (DemangledDataType parameter : parameters) { dfp.addParameter(parameter); @@ -994,7 +1038,8 @@ public class GnuDemanglerParser { */ String nameString = fixupInternalSeparators(demangled).trim(); - DemangledVariable variable = new DemangledVariable((String) null); + DemangledVariable variable = + new DemangledVariable(mangledSource, demangledSource, (String) null); setNameAndNamespace(variable, nameString); return variable; } @@ -1018,15 +1063,14 @@ public class GnuDemanglerParser { int index = names.size() - 1; String rawName = names.get(index); String escapedName = fixupInternalSeparators(rawName); - DemangledType myNamespace = new DemangledType(escapedName); - myNamespace.setOriginalDemangled(rawName); + DemangledType myNamespace = new DemangledType(mangledSource, demangledSource, escapedName); DemangledType namespace = myNamespace; while (--index >= 0) { rawName = names.get(index); escapedName = fixupInternalSeparators(rawName); - DemangledType parentNamespace = new DemangledType(escapedName); - myNamespace.setOriginalDemangled(rawName); + DemangledType parentNamespace = + new DemangledType(mangledSource, demangledSource, escapedName); namespace.setNamespace(parentNamespace); namespace = parentNamespace; } @@ -1037,16 +1081,48 @@ public class GnuDemanglerParser { // Inner Classes //================================================================================================== - private abstract class SpecialPrefixHandler { + private abstract class DemangledObjectBuilder { protected String demangled; + + DemangledObjectBuilder(String demangled) { + this.demangled = demangled; + } + + abstract DemangledObject build(); + } + + private abstract class OperatorHandler extends DemangledObjectBuilder { + + protected Matcher matcher; + + OperatorHandler(String demangled) { + super(demangled); + } + + abstract boolean matches(String s); + + } + + private abstract class SpecialPrefixHandler extends DemangledObjectBuilder { + protected String prefix; protected String name; protected String type; - abstract String getType(); + SpecialPrefixHandler(String demangled) { + super(demangled); + } - abstract DemangledObject build(Demangled namespace); + @Override + DemangledObject build() { + + DemangledObject dobj = parseFunctionOrVariable(type); + + return doBuild(dobj); + } + + abstract DemangledObject doBuild(Demangled namespace); @Override public String toString() { @@ -1063,23 +1139,20 @@ public class GnuDemanglerParser { private class ItemInNamespaceHandler extends SpecialPrefixHandler { ItemInNamespaceHandler(String demangled) { + super(demangled); this.demangled = demangled; this.type = demangled; } ItemInNamespaceHandler(String demangled, String prefix, String item) { + super(demangled); this.demangled = demangled; this.prefix = prefix; this.type = item; } @Override - String getType() { - return type; - } - - @Override - DemangledObject build(Demangled namespace) { + DemangledObject doBuild(Demangled namespace) { DemangledObject demangledObject = parseItemInNamespace(type); return demangledObject; } @@ -1088,24 +1161,20 @@ public class GnuDemanglerParser { private class ThunkHandler extends SpecialPrefixHandler { ThunkHandler(String demangled, String prefix, String item) { + super(demangled); this.demangled = demangled; this.prefix = prefix; this.type = item; } @Override - String getType() { - return type; - } - - @Override - DemangledObject build(Demangled demangledObject) { + DemangledObject doBuild(Demangled demangledObject) { DemangledFunction function = (DemangledFunction) demangledObject; function.setSignature(type); function.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); - DemangledThunk thunk = new DemangledThunk(function); + DemangledThunk thunk = new DemangledThunk(mangledSource, demangledSource, function); if (prefix.contains(COVARIANT_RETURN_THUNK)) { thunk.setCovariantReturnThunk(); } @@ -1118,21 +1187,19 @@ public class GnuDemanglerParser { private class TypeInfoNameHandler extends SpecialPrefixHandler { TypeInfoNameHandler(String demangled, String prefix) { + super(demangled); this.demangled = demangled; this.prefix = prefix; - } - @Override - String getType() { String classname = demangled.substring(prefix.length()).trim(); - type = classname; - return type; + this.type = classname; } @Override - DemangledObject build(Demangled namespace) { + DemangledObject doBuild(Demangled namespace) { DemangledString demangledString = - new DemangledString("typeinfo-name", type, -1/*unknown length*/, false); + new DemangledString(mangledSource, demangledSource, "typeinfo-name", type, + -1/*unknown length*/, false); demangledString.setSpecialPrefix("typeinfo name for "); String namespaceString = fixupInternalSeparators(type); setNamespace(demangledString, namespaceString); @@ -1143,6 +1210,7 @@ public class GnuDemanglerParser { private class AddressTableHandler extends SpecialPrefixHandler { AddressTableHandler(String demangled, String prefix, String type) { + super(demangled); this.demangled = demangled; this.prefix = prefix; this.type = type; @@ -1155,10 +1223,6 @@ public class GnuDemanglerParser { int delta = oldLength - this.demangled.length(); this.type = type.substring(0, type.length() - delta); } - } - - @Override - String getType() { /* Samples: @@ -1176,31 +1240,26 @@ public class GnuDemanglerParser { */ int pos = prefix.trim().lastIndexOf(' '); name = prefix.substring(0, pos).replace(' ', '-'); - return type; } @Override - DemangledObject build(Demangled namespace) { - DemangledAddressTable addressTable = new DemangledAddressTable(name, true); + DemangledObject doBuild(Demangled namespace) { + DemangledAddressTable addressTable = + new DemangledAddressTable(mangledSource, demangled, name, true); addressTable.setNamespace(namespace); return addressTable; } } - private abstract class OperatorHandler { - - protected Matcher matcher; - - abstract boolean matches(String s); - - abstract DemangledObject build(); - } - private class OverloadOperatorHandler extends OperatorHandler { + OverloadOperatorHandler(String demangled) { + super(demangled); + } + @Override - boolean matches(String demangled) { - matcher = OVERLOAD_OPERATOR_PATTERN.matcher(demangled); + boolean matches(String text) { + matcher = OVERLOAD_OPERATOR_PATTERN.matcher(text); return matcher.matches(); } @@ -1220,22 +1279,35 @@ public class GnuDemanglerParser { // prefix: return_type operator operator_chars[templates] // (everything before the parameters) - String operatorPrefix = matcher.group(1); - //String operatorChars = matcher.group(2); - String parametersText = matcher.group(3); - //String trailing = matcher.group(4); + String returnTypeText = matcher.group(1); + String operatorPrefix = matcher.group(2); + //String operatorChars = matcher.group(3); + String templates = matcher.group(4); + String parametersText = matcher.group(5); + //String trailing = matcher.group(6); - String returnTypeText = "undefined"; String operatorName = operatorPrefix; operatorPrefix = fixupInternalSeparators(operatorPrefix); + + if (returnTypeText == null) { + returnTypeText = "undefined"; + } returnTypeText = fixupInternalSeparators(returnTypeText); DemangledDataType returnType = createTypeInNamespace(returnTypeText); - DemangledFunction function = new DemangledFunction((String) null); + DemangledFunction function = + new DemangledFunction(mangledSource, demangledSource, (String) null); function.setOverloadedOperator(true); function.setReturnType(returnType); + if (!StringUtils.isBlank(templates)) { + int templateIndex = operatorName.lastIndexOf(templates); + operatorName = operatorName.substring(0, templateIndex); + DemangledTemplate demangledTemplate = parseTemplate(templates); + function.setTemplate(demangledTemplate); + } + operatorName = fixupInternalSeparators(operatorName); setNameAndNamespace(function, operatorName); @@ -1250,9 +1322,13 @@ public class GnuDemanglerParser { private class ConversionOperatorHandler extends OperatorHandler { + ConversionOperatorHandler(String demangled) { + super(demangled); + } + @Override - boolean matches(String demangled) { - matcher = CONVERSION_OPERATOR_PATTERN.matcher(demangled); + boolean matches(String text) { + matcher = CONVERSION_OPERATOR_PATTERN.matcher(text); return matcher.matches(); } @@ -1266,13 +1342,14 @@ public class GnuDemanglerParser { String fullReturnType = matcher.group(2); boolean isConst = false; - int index = fullReturnType.indexOf(CONST_KEYWORD); + int index = fullReturnType.indexOf(CONST); if (index != -1) { - fullReturnType = fullReturnType.replace(CONST_KEYWORD, ""); + fullReturnType = fullReturnType.replace(CONST, ""); isConst = true; } - DemangledFunction method = new DemangledFunction((String) null); + DemangledFunction method = + new DemangledFunction(mangledSource, demangledSource, (String) null); DemangledDataType returnType = parseDataType(fullReturnType); if (isConst) { returnType.setConst(); @@ -1309,6 +1386,10 @@ public class GnuDemanglerParser { private class NewOrDeleteOperatorHandler extends OperatorHandler { + NewOrDeleteOperatorHandler(String demangled) { + super(demangled); + } + @Override boolean matches(String demangler) { matcher = NEW_DELETE_OPERATOR_PATTERN.matcher(demangler); @@ -1323,9 +1404,11 @@ public class GnuDemanglerParser { String arrayBrackets = matcher.group(3); String parametersText = matcher.group(4); - DemangledFunction function = new DemangledFunction((String) null); + DemangledFunction function = + new DemangledFunction(mangledSource, demangledSource, (String) null); function.setOverloadedOperator(true); - DemangledDataType returnType = new DemangledDataType("void"); + DemangledDataType returnType = + new DemangledDataType(mangledSource, demangledSource, "void"); if (operatorName.startsWith("new")) { returnType.incrementPointerLevels(); } @@ -1392,7 +1475,7 @@ public class GnuDemanglerParser { public String toString() { ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE); return builder - .append("texf", text) + .append("text", text) .append("paramStart", paramStart) .append("paramEnd", paramEnd) .toString(); diff --git a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParser2Test.java b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParser2Test.java new file mode 100644 index 0000000000..b0f15a6a73 --- /dev/null +++ b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParser2Test.java @@ -0,0 +1,621 @@ +/* ### + * 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 ghidra.app.util.demangler; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import generic.test.AbstractGenericTest; +import ghidra.app.util.demangler.gnu.GnuDemanglerParser; + +public class GnuDemanglerParser2Test extends AbstractGenericTest { + + private GnuDemanglerParser parser = new GnuDemanglerParser(); + + //@Test + public void test1() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypeC1Eii", "OpTestType::OpTestType(int, int)"); + String name = object.getName(); + assertEquals("", name); + } + + //@Test + public void test2() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypeC2Eii", "OpTestType::OpTestType(int, int)"); + String name = object.getName(); + assertEquals("", name); + } + + @Test + public void test3() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypeclEf", "OpTestType::operator()(float)"); + String name = object.getName(); + assertEquals("operator()", name); + } + + @Test + public void test4() { + + DemangledObject object = parser.parse("_ZN10OpTestTypeclEi", "OpTestType::operator()(int)"); + String name = object.getName(); + assertEquals("operator()", name); + } + + //@Test + public void test5() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypecvN16Names", "_ZN10OpTestTypecvN16Names"); + String name = object.getName(); + assertEquals("", name); + } + + @Test + public void test6() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypecvPKcEv", "OpTestType::operator char const*()"); + String name = object.getName(); + assertEquals("operator.cast.to.char*", name); + } + + @Test + public void test7() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypedaEPv", "OpTestType::operator delete[](void*)"); + String name = object.getName(); + assertEquals("operator.delete[]", name); + } + + @Test + public void test8() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypedlEPv", "OpTestType::operator delete(void*)"); + String name = object.getName(); + assertEquals("operator.delete", name); + } + + @Test + public void test9() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypemIERKS_", "OpTestType::operator-=(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator-=", name); + } + + @Test + public void test10() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypemiERKS_", "OpTestType::operator-(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator-", name); + } + + @Test + public void test11() { + + DemangledObject object = parser.parse("_ZN10OpTestTypemmEi", "OpTestType::operator--(int)"); + String name = object.getName(); + assertEquals("operator--", name); + } + + @Test + public void test12() { + + DemangledObject object = parser.parse("_ZN10OpTestTypemmEv", "OpTestType::operator--()"); + String name = object.getName(); + assertEquals("operator--", name); + } + + @Test + public void test13() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypenaEm", "OpTestType::operator new[](unsigned long)"); + String name = object.getName(); + assertEquals("operator.new[]", name); + } + + @Test + public void test14() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypenwEm", "OpTestType::operator new(unsigned long)"); + String name = object.getName(); + assertEquals("operator.new", name); + } + + @Test + public void test15() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypepLERKS_", "OpTestType::operator+=(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator+=", name); + } + + @Test + public void test16() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypeplERKS_", "OpTestType::operator+(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator+", name); + } + + @Test + public void test17() { + + DemangledObject object = parser.parse("_ZN10OpTestTypeppEi", "OpTestType::operator++(int)"); + String name = object.getName(); + assertEquals("operator++", name); + } + + @Test + public void test18() { + + DemangledObject object = parser.parse("_ZN10OpTestTypeppEv", "OpTestType::operator++()"); + String name = object.getName(); + assertEquals("operator++", name); + } + +//-------------------- +//TODO: for the following, determine what arguments are needed. + + @Test + public void testOperatorNew() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypenwEm", "OpTestType::operator new(unsigned long)"); + String name = object.getName(); + assertEquals("operator.new", name); + } + + @Test + public void testOperatorDelete() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypedlEPv", "OpTestType::operator delete(void*)"); + String name = object.getName(); + assertEquals("operator.delete", name); + } + + @Test + public void testOperatorAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator=()"); + String name = object.getName(); + assertEquals("operator=", name); + } + + @Test + public void testOperatorRightShift() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator>>()"); + String name = object.getName(); + assertEquals("operator>>", name); + } + + @Test + public void testOperatorLeftShift() { + + DemangledObject object = + parser.parse("_ZN11myContainerIiElsEi", "myContainer::operator<<(int)"); + String name = object.getName(); + assertEquals("operator<<", name); + } + + @Test + public void testOperatorLeftShiftTemplated() { + + DemangledObject object = parser.parse("_ZN11myContainerIiElsIdEEbT_", + "bool myContainer::operator<< (double)"); + String name = object.getName(); + assertEquals("operator<<", name); + assertEquals("bool myContainer::operator<<(double)", + object.getSignature()); + } + + @Test + public void testOperatorLogicalNot() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator!()"); + String name = object.getName(); + assertEquals("operator!", name); + } + + @Test + public void testOperatorEquality() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator==()"); + String name = object.getName(); + assertEquals("operator==", name); + } + + @Test + public void testOperatorInequality() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator!=()"); + String name = object.getName(); + assertEquals("operator!=", name); + } + + @Test + public void testOperatorArraySubscript() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator[]()"); + String name = object.getName(); + assertEquals("operator[]", name); + } + + @Test + public void testOperatorTypeCast() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypecvPKcEv", "OpTestType::operator char const*()"); + String name = object.getName(); + assertEquals("operator.cast.to.char*", name); + } + + @Test + public void testOperatorTypeCast_WithNamespace() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypecvN16NamespaceOpTest116NamespaceOpTest210CastToTypeEEv", + "OpTestType::operator NamespaceOpTest1::NamespaceOpTest2::CastToType()"); + assertName(object, "operator.cast.to.CastToType", "OpTestType"); + assertEquals( + "NamespaceOpTest1::NamespaceOpTest2::CastToType OpTestType::operator.cast.to.CastToType(void)", + object.getSignature()); + } + + @Test + public void testOperatorPointerDereference() { + DemangledObject object = parser.parse("fake", "OpTestType::operator->()"); + String name = object.getName(); + assertEquals("operator->", name); + } + + @Test + public void testOperatorMultiplication() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator*()"); + String name = object.getName(); + assertEquals("operator*", name); + } + + //TODO: If laying down function signatures, then we need to investigate whether we can + // determine prefix vs. postfix increment. Postfix will have an argument and prefix will not. + // Same for prefix vs. postfix decrement. + @Test + public void testOperatorPrefixIncrement() { + + DemangledObject object = parser.parse("_ZN10OpTestTypeppEv", "OpTestType::operator++()"); + String name = object.getName(); + assertEquals("operator++", name); + } + + @Test + public void testOperatorPostfixIncrement() { + + DemangledObject object = parser.parse("_ZN10OpTestTypeppEi", "OpTestType::operator++(int)"); + String name = object.getName(); + assertEquals("operator++", name); + } + + @Test + public void testOperatorPrefixDecrement() { + + DemangledObject object = parser.parse("_ZN10OpTestTypemmEv", "OpTestType::operator--()"); + String name = object.getName(); + assertEquals("operator--", name); + } + + @Test + public void testOperatorPostfixDecrement() { + + DemangledObject object = parser.parse("_ZN10OpTestTypemmEi", "OpTestType::operator--(int)"); + String name = object.getName(); + assertEquals("operator--", name); + } + + @Test + public void testOperatorSubtraction() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypemiERKS_", "OpTestType::operator-(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator-", name); + } + + @Test + public void testOperatorAddition() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypeplERKS_", "OpTestType::operator+(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator+", name); + } + + @Test + public void testOperatorAddressOf() { + + DemangledObject object = parser.parse("_ZN10SmallClassadEv", "SmallClass::operator&()"); + String name = object.getName(); + assertEquals("operator&", name); + } + + @Test + public void testOperatorPointerToMemberSelection() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator->*()"); + String name = object.getName(); + assertEquals("operator->*", name); + } + + @Test + public void testOperatorDivision() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator/()"); + String name = object.getName(); + assertEquals("operator/", name); + } + + @Test + public void testOperatorModulus() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator%()"); + String name = object.getName(); + assertEquals("operator%", name); + } + + @Test + public void testOperatorLessThan() { + + DemangledObject object = + parser.parse("_ZN11myContainerIiEltEi", "myContainer::operator<(int)"); + String name = object.getName(); + assertEquals("operator<", name); + } + + @Test + public void testOperatorLessThanTemplated() { + + DemangledObject object = parser.parse("_ZltI11myContainerIiEEbRKT_S4_", + "bool operator< >(myContainer const&, myContainer const&)"); + String name = object.getName(); + assertEquals("operator<", name); + assertEquals( + "bool operator<>(myContainer const &,myContainer const &)", + object.getSignature()); + } + + @Test + public void testOperatorLessThanOrEqualTo() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator<=()"); + String name = object.getName(); + assertEquals("operator<=", name); + } + + @Test + public void testOperatorGreaterThan() { + + DemangledObject object = parser.parse("_ZgtRK10complex_ldS1_", + "operator>(complex_ld const&, complex_ld const&)"); + String name = object.getName(); + assertEquals("operator>", name); + } + + @Test + public void testOperatorGreaterThanOrEqualTo() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator>=()"); + String name = object.getName(); + assertEquals("operator>=", name); + } + + @Test + public void testOperatorComma() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator,()"); + String name = object.getName(); + assertEquals("operator,", name); + } + + @Test + public void testOperatorFunctionCall() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypeclEf", "OpTestType::operator()(float)"); + String name = object.getName(); + assertEquals("operator()", name); + } + + @Test + public void testOperatorOnesComplement() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator~()"); + String name = object.getName(); + assertEquals("operator~", name); + } + + @Test + public void testOperatorExclusiveOr() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator^()"); + String name = object.getName(); + assertEquals("operator^", name); + } + + @Test + public void testOperatorBitwiseInclusiveOr() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator|()"); + String name = object.getName(); + assertEquals("operator|", name); + } + + @Test + public void testOperatorLogicalAnd() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator&&()"); + String name = object.getName(); + assertEquals("operator&&", name); + } + + @Test + public void testOperatorLogicalOr() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator||()"); + String name = object.getName(); + assertEquals("operator||", name); + } + + @Test + public void testOperatorMultiplicationAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator*=()"); + String name = object.getName(); + assertEquals("operator*=", name); + } + + @Test + public void testOperatorAdditionAssignment() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypepLERKS_", "OpTestType::operator+=(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator+=", name); + } + + @Test + public void testOperatorSubtractionAssignment() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypemIERKS_", "OpTestType::operator-=(OpTestType const&)"); + String name = object.getName(); + assertEquals("operator-=", name); + } + + @Test + public void testOperatorDivisionAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator/=()"); + String name = object.getName(); + assertEquals("operator/=", name); + } + + @Test + public void testOperatorModulusAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator%=()"); + String name = object.getName(); + assertEquals("operator%=", name); + } + + @Test + public void testOperatorRightShiftAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator>>=()"); + String name = object.getName(); + assertEquals("operator>>=", name); + } + + @Test + public void testOperatorLeftShiftAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator<<=()"); + String name = object.getName(); + assertEquals("operator<<=", name); + } + + @Test + public void testOperatorBitwiseAndAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator&=()"); + String name = object.getName(); + assertEquals("operator&=", name); + } + + @Test + public void testOperatorBitwiseOrAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator|=()"); + String name = object.getName(); + assertEquals("operator|=", name); + } + + @Test + public void testOperatorExclusiveOrAssignment() { + + DemangledObject object = parser.parse("fake", "OpTestType::operator^=()"); + String name = object.getName(); + assertEquals("operator^=", name); + } + + @Test + public void testOperatorNewArray() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypenaEm", "OpTestType::operator new[](unsigned long)"); + String name = object.getName(); + assertEquals("operator.new[]", name); + } + + @Test + public void testOperatorDeleteArray() { + + DemangledObject object = + parser.parse("_ZN10OpTestTypedaEPv", "OpTestType::operator delete[](void*)"); + String name = object.getName(); + assertEquals("operator.delete[]", name); + } + + @Test + public void testOperatorUserDefinedLiteral() { + + DemangledObject object = + parser.parse("_Zli5_initPKcm", "operator\"\" _init(char const*, unsigned long)"); + String name = object.getName(); + assertEquals("operator\"\"__init", name); + } + + private void assertName(DemangledObject demangledObj, String name, String... namespaces) { + + assertEquals("Unexpected demangled name", name, demangledObj.getName()); + Demangled namespace = demangledObj.getNamespace(); + for (int i = namespaces.length - 1; i >= 0; i--) { + String expectedName = namespaces[i]; + assertNotNull("Namespace mismatch", namespace); + String actualName = namespace.getNamespaceName(); + assertEquals(expectedName, actualName); + namespace = namespace.getNamespace(); + } + assertNull("Namespace mismatch", namespace); + } +} diff --git a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java index 05f471333a..1434c17590 100644 --- a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java +++ b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java @@ -38,14 +38,22 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { } @Test - public void testOverloadedShiftOperatorParsingBug() { - parser = new GnuDemanglerParser(); - DemangledObject object = parser.parse(null, - "std::basic_istream >& " + - "std::operator>> >" + - "(std::basic_istream >&, char&)"); - String name = object.getName(); - assertEquals("operator>>>", name); + public void testParse_ArrayPointerReferencePattern_ConstArray() throws Exception { + + // bob(int const[8] (*) [12]) + + String demangled = + "bob(int const[8] (*) [12])"; + DemangledObject object = parser.parse("fake", demangled); + assertType(object, DemangledFunction.class); + assertName(object, "bob"); + + DemangledFunction function = (DemangledFunction) object; + List parameters = function.getParameters(); + assertEquals(1, parameters.size()); + DemangledDataType p1 = parameters.get(0); + assertEquals("bob(int const[8] (*) [12])", p1.getOriginalDemangled()); + assertEquals("undefined bob(int *[])", object.getSignature(false)); } @Test @@ -532,6 +540,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { // String mangled = "CalcPortExposedRect__13LScrollerViewCFR4Rectb"; + // use an older demangler; the current demangler cannot handle this string process = GnuDemanglerNativeProcess .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); @@ -556,6 +565,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { // String mangled = "__dt__Q26MsoDAL9VertFrameFv"; + // use an older demangler; the current demangler cannot handle this string process = GnuDemanglerNativeProcess .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); @@ -596,6 +606,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { // String mangled = "GetColWidths__13CDataRendererCFRA7_s"; + // use an older demangler; the current demangler cannot handle this string process = GnuDemanglerNativeProcess .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); @@ -620,6 +631,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { // String mangled = "GetColWidths__13CDataRendererCFPA7_s"; + // use an older demangler; the current demangler cannot handle this string process = GnuDemanglerNativeProcess .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); @@ -678,6 +690,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { // String mangled = "_gmStage2__FP12SECTION_INFOPiPA12_iiPCs"; + // use an older demangler; the current demangler cannot handle this string process = GnuDemanglerNativeProcess .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); @@ -712,6 +725,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { // String mangled = "__ct__Q24CStr6BufferFR4CStrUl"; + // use an older demangler; the current demangler cannot handle this string process = GnuDemanglerNativeProcess .getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24); @@ -805,6 +819,22 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertEquals("Magick::Coordinate const &", parameters.get(1).getSignature()); } + @Test + public void testOverloadedShiftOperatorParsingBug() { + parser = new GnuDemanglerParser(); + DemangledObject object = parser.parse(null, + "std::basic_istream >& " + + "std::operator>> >" + + "(std::basic_istream >&, char&)"); + String name = object.getName(); + assertEquals("operator>>", name); + assertEquals( + "std::basic_istream>& " + + "std::operator>>>" + + "(std::basic_istream> &,char &)", + object.getSignature()); + } + @Test public void testOperator_Functor() throws Exception { @@ -1399,6 +1429,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertEquals("undefined wrap_360_cd(int)", signature); } + @Test + public void testGetDataType_LongLong() throws Exception { + assertNotNull( + new DemangledDataType("fake", "fake", DemangledDataType.LONG_LONG).getDataType(null)); + } + private void assertType(Demangled o, Class c) { assertTrue("Wrong demangled type. " + "\nExpected " + c + "; " + 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 075cef33ea..f9d4dc26ec 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 @@ -602,7 +602,10 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { if (descriptorName == null) { return null; } - DemangledType typeNamespace = new DemangledType(descriptorName); + + String demangledSource = mdComplexType.toString(); + DemangledType typeNamespace = + new DemangledType(originalTypeName, demangledSource, descriptorName); DemangledType parentNamespace = getParentNamespace(); // Can be null; if (parentNamespace != null) { typeNamespace.setNamespace(parentNamespace); 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 f2c4850a8c..431bce0319 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 @@ -76,10 +76,10 @@ public class MicrosoftDemangler implements Demangler { return object; } catch (MDException e) { - DemangledException gde = + DemangledException de = new DemangledException("Unable to demangle symbol: " + mangled); - gde.initCause(e); - throw gde; + de.initCause(e); + throw de; } } } diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java index 149f9f7a7a..730e9f927a 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java @@ -38,6 +38,9 @@ public class MDMangGhidra extends MDMang { private DemangledObject objectResult; private DemangledDataType dataTypeResult; + private String mangledSource; + private String demangledSource; + public DemangledObject getObject() { return objectResult; } @@ -46,31 +49,6 @@ public class MDMangGhidra extends MDMang { return dataTypeResult; } - public DemangledType processNamespace(MDQualifiedName qualifiedName) { - return processNamespace(qualifiedName.getQualification()); - } - - private DemangledType processNamespace(MDQualification qualification) { - Iterator it = qualification.iterator(); - if (!it.hasNext()) { - return null; - } - - MDQualifier qual = it.next(); - DemangledType type = new DemangledType(qual.toString()); - DemangledType parentType = type; - while (it.hasNext()) { - qual = it.next(); - DemangledType newType = new DemangledType(qual.toString()); - if (qual.isNested()) { - newType.setMangledString(qual.getNested().getMangled()); - } - parentType.setNamespace(newType); - parentType = newType; - } - return type; - } - @Override public MDParsableItem demangle(String mangledArg, boolean demangleOnlyKnownPatterns) throws MDException { @@ -83,16 +61,47 @@ public class MDMangGhidra extends MDMang { return null; } } + + this.mangledSource = mangledArg; + MDParsableItem returnedItem = super.demangle(mangledArg, true); + + this.demangledSource = item.toString(); + objectResult = processItem(); - if (objectResult != null) { - objectResult.setMangledString(mangledArg); - // Make our version of the demangled string available (could be large). - objectResult.setOriginalDemangled(item.toString()); - } return returnedItem; } + public DemangledType processNamespace(MDQualifiedName qualifiedName) { + return processNamespace(qualifiedName.getQualification()); + } + + private DemangledType processNamespace(MDQualification qualification) { + Iterator it = qualification.iterator(); + if (!it.hasNext()) { + return null; + } + + MDQualifier qual = it.next(); + DemangledType type = new DemangledType(mangledSource, demangledSource, qual.toString()); + DemangledType parentType = type; + while (it.hasNext()) { + qual = it.next(); + DemangledType newType; + if (qual.isNested()) { + String subMangled = qual.getNested().getMangled(); + newType = new DemangledType(subMangled, demangledSource, qual.toString()); + } + else { + newType = + new DemangledType(mangledSource, demangledSource, qual.toString()); + } + parentType.setNamespace(newType); + parentType = newType; + } + return type; + } + private DemangledObject processItem() { objectResult = null; if (item instanceof MDObjectReserved) { @@ -137,7 +146,7 @@ public class MDMangGhidra extends MDMang { } //TODO: put other objectReserved derivative types here and return something that Ghidra can use. else { - object = new DemangledUnknown(); + object = new DemangledUnknown(mangledSource, demangledSource, null); } return object; } @@ -175,10 +184,10 @@ public class MDMangGhidra extends MDMang { MDType mdtype = variableInfo.getMDType(); DemangledDataType dt = processDataType(null, (MDDataType) mdtype); if ("std::nullptr_t".equals(dt.getName())) { - variable = new DemangledVariable(""); + variable = new DemangledVariable(mangledSource, demangledSource, ""); } else { - variable = new DemangledVariable( + variable = new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); variable.setNamespace(processNamespace(objectCPP.getQualfication())); } @@ -200,7 +209,7 @@ public class MDMangGhidra extends MDMang { } else if (typeinfo instanceof MDFunctionInfo) { DemangledFunction function = - new DemangledFunction(objectCPP.getName()); + new DemangledFunction(mangledSource, demangledSource, objectCPP.getName()); function.setNamespace(processNamespace(objectCPP.getQualfication())); resultObject = function; objectResult = processFunction((MDFunctionInfo) typeinfo, function); @@ -229,7 +238,7 @@ public class MDMangGhidra extends MDMang { else if (typeinfo instanceof MDVxTable) { //Includes VFTable, VBTable, and RTTI4 MDVxTable vxtable = (MDVxTable) typeinfo; DemangledVariable variable = - new DemangledVariable(objectCPP.getName()); + new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); variable.setNamespace(processNamespace(objectCPP.getQualfication())); variable.setConst(vxtable.isConst()); variable.setVolatile(vxtable.isVolatile()); @@ -241,7 +250,7 @@ public class MDMangGhidra extends MDMang { } else if (typeinfo instanceof AbstractMDMetaClass) { //Includes all RTTI, except RTTI4 DemangledVariable variable = - new DemangledVariable(objectCPP.getName()); + new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); variable.setNamespace(processNamespace(objectCPP.getQualfication())); resultObject = variable; // The following code would be an alternative, depending on whether we get @@ -250,7 +259,7 @@ public class MDMangGhidra extends MDMang { } else if (typeinfo instanceof MDGuard) { DemangledVariable variable = - new DemangledVariable(objectCPP.getName()); + new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); variable.setNamespace(processNamespace(objectCPP.getQualfication())); resultObject = variable; // The following code would be an alternative, depending on whether we get @@ -260,7 +269,7 @@ public class MDMangGhidra extends MDMang { else { // Any others (e.g., case '9') DemangledVariable variable = - new DemangledVariable(objectCPP.getName()); + new DemangledVariable(mangledSource, demangledSource, objectCPP.getName()); variable.setNamespace(processNamespace(objectCPP.getQualfication())); resultObject = variable; // The following code would be an alternative, depending on whether we get @@ -287,13 +296,14 @@ public class MDMangGhidra extends MDMang { String baseName = objectCPP.getName(); if (objectCPP.isString()) { MDString mstring = objectCPP.getMDString(); - DemangledString demangledString = new DemangledString(mstring.getName(), - mstring.toString(), mstring.getLength(), mstring.isUnicode()); + DemangledString demangledString = + new DemangledString(mangledSource, demangledSource, mstring.getName(), + mstring.toString(), mstring.getLength(), mstring.isUnicode()); resultObject = demangledString; } else if (baseName.length() != 0) { DemangledVariable variable; - variable = new DemangledVariable(baseName); + variable = new DemangledVariable(mangledSource, demangledSource, baseName); variable.setNamespace(processNamespace(objectCPP.getQualfication())); resultObject = variable; } @@ -314,7 +324,8 @@ public class MDMangGhidra extends MDMang { // doesn't match // well to the current DemangledObject hierarchy. private DemangledVariable processTemplate(MDTemplateNameAndArguments template) { - DemangledVariable variable = new DemangledVariable(template.toString()); + DemangledVariable variable = + new DemangledVariable(mangledSource, demangledSource, template.toString()); // NO NAMESPACE for high level template: variable.setNamespace(XXX); // DemangledTemplate objectTemplate = new DemangledTemplate(); // DemangledDataType dataType = new DemangledDataType((String) null); @@ -397,7 +408,8 @@ public class MDMangGhidra extends MDMang { } private DemangledFunctionPointer processDemangledFunctionPointer(MDPointerType pointerType) { - DemangledFunctionPointer functionPointer = new DemangledFunctionPointer(); + DemangledFunctionPointer functionPointer = + new DemangledFunctionPointer(mangledSource, demangledSource); MDFunctionType functionType = (MDFunctionType) pointerType.getReferencedType(); functionPointer.setCallingConvention(functionType.getCallingConvention().toString()); functionPointer.setModifier(pointerType.getCVMod().toString()); @@ -432,7 +444,8 @@ public class MDMangGhidra extends MDMang { if (!((refType instanceof MDReferenceType) || (refType instanceof MDDataRefRefType))) { return null; // Not planning on anything else yet. } - DemangledFunctionReference functionReference = new DemangledFunctionReference(); + DemangledFunctionReference functionReference = + new DemangledFunctionReference(mangledSource, demangledSource); MDFunctionType functionType = (MDFunctionType) refType.getReferencedType(); functionReference.setCallingConvention(functionType.getCallingConvention().toString()); functionReference.setModifier(refType.getCVMod().toString()); @@ -447,7 +460,8 @@ public class MDMangGhidra extends MDMang { private DemangledFunctionIndirect processDemangledFunctionIndirect( MDFunctionIndirectType functionIndirectType) { - DemangledFunctionIndirect functionDefinition = new DemangledFunctionIndirect(); + DemangledFunctionIndirect functionDefinition = + new DemangledFunctionIndirect(mangledSource, demangledSource); MDFunctionType functionType = (MDFunctionType) functionIndirectType.getReferencedType(); functionDefinition.setCallingConvention(functionType.getCallingConvention().toString()); functionDefinition.setModifier(functionIndirectType.getCVMod().toString()); @@ -466,7 +480,8 @@ public class MDMangGhidra extends MDMang { // indirect might be clouded between the real, two underlying types. private DemangledFunctionIndirect processDemangledFunctionQuestion( MDModifierType modifierType) { - DemangledFunctionIndirect functionDefinition = new DemangledFunctionIndirect(); + DemangledFunctionIndirect functionDefinition = + new DemangledFunctionIndirect(mangledSource, demangledSource); MDFunctionType functionType = (MDFunctionType) modifierType.getReferencedType(); functionDefinition.setCallingConvention(functionType.getCallingConvention().toString()); functionDefinition.setModifier(modifierType.getCVMod().toString()); @@ -488,7 +503,8 @@ public class MDMangGhidra extends MDMang { private DemangledDataType processDataType(DemangledDataType resultDataType, MDDataType datatype) { if (resultDataType == null) { - resultDataType = new DemangledDataType(datatype.getTypeName()); + resultDataType = + new DemangledDataType(mangledSource, demangledSource, datatype.getTypeName()); } if (datatype.isSpecifiedSigned()) { // Returns true if default signed or specified signed. TODO: There is no place to diff --git a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java index 92da2e0c9a..ae74fe6b5b 100644 --- a/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java +++ b/Ghidra/Features/MicrosoftDmang/src/test/java/mdemangler/MDBaseTestConfiguration.java @@ -15,6 +15,8 @@ */ package mdemangler; +import static org.junit.Assert.*; + import ghidra.util.Msg; import mdemangler.datatype.MDDataType; @@ -65,7 +67,7 @@ public class MDBaseTestConfiguration { * @param mstruth Truth that was output from one of the Microsoft tools (e.g., undname). * @param ghtruth Truth that we would like to see for Ghidra version of the tool. * @param ms2013truth Like mstruth, but from Visual Studio 2013 version of tool. - * @throws Exception + * @throws Exception if any exceptions are thrown */ public void demangleAndTest(String mangledArg, String mdtruth, String mstruth, String ghtruth, String ms2013truth) throws Exception { @@ -105,24 +107,24 @@ public class MDBaseTestConfiguration { // expect to be able to demangle the input (truth not equal to mangleArg), then we // expect the output to be that which we desire ("truth".equals(demangled)). if ((truth.equals(mangledArg)) && isMangled(mangledArg)) { - assert (demangled.isEmpty()); + assertTrue(demangled.isEmpty()); } else { - assert(truth.equals(demangled)); + assertEquals(truth, demangled); } if (mangledArg.startsWith(".?A")) { - assert ((demangItem != null) && (demangItem instanceof MDDataType)); // Test for data type. + assertTrue((demangItem != null) && (demangItem instanceof MDDataType)); // Test for data type. } } - private boolean isMangled(String mangled) { - if (mangled.charAt(0) == '?') { + private boolean isMangled(String s) { + if (s.charAt(0) == '?') { return true; } - else if (mangled.startsWith("__")) { + else if (s.startsWith("__")) { return true; } - else if ((mangled.charAt(0) == '_') || Character.isUpperCase(mangled.charAt(1))) { + else if ((s.charAt(0) == '_') || Character.isUpperCase(s.charAt(1))) { return true; } return false; diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java index 73f5ebba1d..d3b1f81ddc 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/demangler/DemangledFunctionTest.java @@ -243,6 +243,50 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest assertEquals("ATL", ns.getName(false)); } + @Test + public void testFunctionThisPointer() throws Exception { + + // + // Test a function within a class that has a 'this' pointer + // + + String mangled = + "??$?0V?$A@_NABW4B@C@@@D@E@@@?$F@V?$G@U?$H@Q6A_NABW4B@C@@@Z$0A@@D@E@@_NABW4B@C@@@D@E@@@E@@QAE@ABV?$F@V?$A@_NABW4B@C@@@D@E@@@1@@Z"; + Address addr = addr("0x0101"); + + SymbolTable symbolTable = program.getSymbolTable(); + symbolTable.createLabel(addr, mangled, SourceType.IMPORTED); + + DemangledObject demangled = DemanglerUtil.demangle(mangled); + assertTrue(demangled instanceof DemangledFunction); + assertTrue(demangled.applyTo(program, addr, new DemanglerOptions(), TaskMonitor.DUMMY)); + + String className = + "F,bool,enum_C::B_const&>_>"; + String functionName = + className + "_>"; + + Function function = assertFunction(functionName, addr); + assertNoBookmarkAt(addr); + + Symbol[] symbols = symbolTable.getSymbols(addr); + assertEquals(2, symbols.length); + assertEquals(functionName, symbols[0].getName()); + assertEquals(mangled, symbols[1].getName()); + + // Check for the Class 'this' pointer + Parameter[] parameters = function.getParameters(); + assertEquals(2, parameters.length); + Parameter p1 = parameters[0]; + assertEquals("this", p1.getName()); + assertEquals(className + " *", p1.getDataType().toString()); + + Namespace ns = symbols[0].getParentNamespace(); + assertEquals(className, ns.getName(false)); + ns = ns.getParentNamespace(); + assertEquals("E", ns.getName(false)); + } + /* * Test that the DemangledFunction will properly create a cascade of namespaces for * functions that live inside of a class that lives inside of a namespace. @@ -343,11 +387,12 @@ public class DemangledFunctionTest extends AbstractGhidraHeadlessIntegrationTest assertEquals(SymbolType.NAMESPACE, ns.getSymbol().getSymbolType()); } - private void assertFunction(String name, Address addr) { + private Function assertFunction(String name, Address addr) { FunctionManager fm = program.getFunctionManager(); Function function = fm.getFunctionAt(addr); assertNotNull("Expected function to get created at " + addr, function); assertEquals(name, function.getName()); + return function; } private Address addr(String addr) {