From 0b1b8d5a36748955749005ac5682d3d8bfd61d16 Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Thu, 15 Sep 2022 15:28:54 -0400 Subject: [PATCH 1/2] GP-2557 Change demangler to use standardized anonymous function definition names --- .../ghidra/app/util/DataTypeNamingUtil.java | 12 ++++----- .../dwarf4/next/DWARFDataTypeImporter.java | 12 ++++----- ...ctDemangledFunctionDefinitionDataType.java | 6 ++++- .../app/util/demangler/DemangledDataType.java | 27 +++++++++++-------- .../AbstractFunctionTypeApplier.java | 2 +- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java index b86307c98d..2a58dc1ed2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java @@ -20,29 +20,27 @@ import ghidra.util.InvalidNameException; public class DataTypeNamingUtil { + private static final String ANONYMOUS_FUNCTION_DEF_PREFIX = "_func"; + private DataTypeNamingUtil() { // no construct } /** * Generate a simple mangled function definition name and apply it to the specified functionDefinition. + * Generated name will start with {@code _function_}. * @param functionDefinition function definition whose name should be set - * @param namePrefix prefix to be applied to generated name. An underscore will separate this prefix from the - * remainder of the mangled name. If null specified a prefix of "_function" will be used. * @return name applied to functionDefinition * @throws IllegalArgumentException if generated name contains unsupported characters */ public static String setMangledAnonymousFunctionName( - FunctionDefinitionDataType functionDefinition, String namePrefix) + FunctionDefinitionDataType functionDefinition) throws IllegalArgumentException { DataType returnType = functionDefinition.getReturnType(); ParameterDefinition[] parameters = functionDefinition.getArguments(); - if (namePrefix == null) { - namePrefix = "_function"; - } - StringBuilder sb = new StringBuilder(namePrefix); + StringBuilder sb = new StringBuilder(ANONYMOUS_FUNCTION_DEF_PREFIX); GenericCallingConvention convention = functionDefinition.getGenericCallingConvention(); if (convention != null && convention != GenericCallingConvention.unknown) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java index 7e617924df..2a7ca73815 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java @@ -15,14 +15,12 @@ */ package ghidra.app.util.bin.format.dwarf4.next; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.DW_AT_count; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.DW_AT_upper_bound; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_subrange_type; - -import java.util.*; -import java.util.stream.Collectors; +import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -311,7 +309,7 @@ public class DWARFDataTypeImporter { if (dni.isAnon() && mangleAnonFuncNames) { String mangledName = - DataTypeNamingUtil.setMangledAnonymousFunctionName(funcDef, dni.getName()); + DataTypeNamingUtil.setMangledAnonymousFunctionName(funcDef); dni = dni.replaceName(mangledName, dni.getOriginalName()); } 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 index 0c8cc10813..3a099ce7c6 100644 --- 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 @@ -20,6 +20,7 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; +import ghidra.app.util.DataTypeNamingUtil; import ghidra.program.model.data.*; import ghidra.program.model.symbol.Namespace; @@ -314,7 +315,8 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang @Override public DataType getDataType(DataTypeManager dataTypeManager) { - FunctionDefinitionDataType fddt = new FunctionDefinitionDataType(getName()); + FunctionDefinitionDataType fddt = + new FunctionDefinitionDataType(DEMANGLER_ANONYMOUS_FUNCTION_CATEGORY_PATH, getName()); if (returnType != null) { fddt.setReturnType(returnType.getDataType(dataTypeManager)); @@ -322,6 +324,8 @@ public abstract class AbstractDemangledFunctionDefinitionDataType extends Demang setParameters(fddt, dataTypeManager); + DataTypeNamingUtil.setMangledAnonymousFunctionName(fddt); + DataType dt = DemangledDataType.findDataType(dataTypeManager, namespace, getName()); if (dt == null || !(dt instanceof FunctionDefinitionDataType)) { dt = fddt; 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 02164b0173..64bfc0568c 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 @@ -32,6 +32,11 @@ import ghidra.program.model.symbol.Namespace; */ public class DemangledDataType extends DemangledType { + protected static final String DEMANGLER_ROOT_CATEGORY_PATH = "/Demangler"; + + protected static final CategoryPath DEMANGLER_ANONYMOUS_FUNCTION_CATEGORY_PATH = + new CategoryPath(DEMANGLER_ROOT_CATEGORY_PATH + "/!_anon_funcs_"); + private static final Pattern ARRAY_SUBSCRIPT_PATTERN = Pattern.compile("\\[\\d*\\]"); public static final char SPACE = ' '; @@ -145,7 +150,7 @@ public class DemangledDataType extends DemangledType { } else if (isUnion()) { if (baseType == null || !(baseType instanceof Union)) { - dt = new UnionDataType(getDemanglerCategoryPath(name, getNamespace()), name); + dt = new UnionDataType(getDemanglerCategoryPath(getNamespace()), name); } } else if (isEnum()) { @@ -153,25 +158,25 @@ public class DemangledDataType extends DemangledType { 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, + dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name, dataTypeManager.getDataOrganization().getIntegerSize()); } else if (CHAR.equals(enumType) || UNSIGNED_CHAR.equals(enumType)) { - dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, + dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name, dataTypeManager.getDataOrganization().getCharSize()); } else if (SHORT.equals(enumType) || UNSIGNED_SHORT.equals(enumType)) { - dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, + dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name, dataTypeManager.getDataOrganization().getShortSize()); } else if (LONG.equals(enumType) || UNSIGNED_LONG.equals(enumType)) { - dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, + dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name, dataTypeManager.getDataOrganization().getLongSize()); } else { - dt = new EnumDataType(getDemanglerCategoryPath(name, getNamespace()), name, + dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name, dataTypeManager.getDataOrganization().getIntegerSize()); } } @@ -187,7 +192,7 @@ public class DemangledDataType extends DemangledType { // I don't know what this is // If it isn't pointed to, or isn't a referent, then assume typedef. if (!(isReference() || isPointer())) { // Unknown type - dt = new TypedefDataType(getDemanglerCategoryPath(name, getNamespace()), name, + dt = new TypedefDataType(getDemanglerCategoryPath(getNamespace()), name, new DWordDataType()); } else { @@ -394,7 +399,7 @@ public class DemangledDataType extends DemangledType { return true; } - private static String getNamespacePath(String dtName, Demangled namespace) { + private static String getNamespacePath(Demangled namespace) { Demangled ns = namespace; String namespacePath = ""; while (ns != null) { @@ -404,8 +409,8 @@ public class DemangledDataType extends DemangledType { return namespacePath; } - private static CategoryPath getDemanglerCategoryPath(String dtName, Demangled namespace) { - return new CategoryPath("/Demangler" + getNamespacePath(dtName, namespace)); + protected static CategoryPath getDemanglerCategoryPath(Demangled namespace) { + return new CategoryPath(DEMANGLER_ROOT_CATEGORY_PATH + getNamespacePath(namespace)); } static Structure createPlaceHolderStructure(String dtName, Demangled namespace) { @@ -414,7 +419,7 @@ public class DemangledDataType extends DemangledType { } StructureDataType structDT = new StructureDataType(dtName, 0); structDT.setDescription("PlaceHolder Structure"); - structDT.setCategoryPath(getDemanglerCategoryPath(dtName, namespace)); + structDT.setCategoryPath(getDemanglerCategoryPath(namespace)); return structDT; } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java index 9eadb40511..6952549667 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java @@ -200,7 +200,7 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier { argsListApplier.applyTo(this); } setCallingConvention(applicator, callingConvention, hasThisPointer); - DataTypeNamingUtil.setMangledAnonymousFunctionName(functionDefinition, "_func"); + DataTypeNamingUtil.setMangledAnonymousFunctionName(functionDefinition); setApplied(); // resolvedDataType = applicator.resolveHighUse(dataType); From de4affbc9b1fe16684bf16bdf78d4447d6d8eb45 Mon Sep 17 00:00:00 2001 From: ghizard <50744617+ghizard@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:53:37 -0400 Subject: [PATCH 2/2] GP-2557 - Demangler anon funcs - CategoryPath changes --- .../ghidra/app/util/DataTypeNamingUtil.java | 8 +-- .../app/util/demangler/DemangledDataType.java | 27 ++++----- .../program/model/data/CategoryPath.java | 48 ++++++++++++++-- .../program/model/data/CategoryPathTest.java | 57 +++++++++++++++++++ 4 files changed, 115 insertions(+), 25 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java index 2a58dc1ed2..a9a33ca3ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java @@ -19,16 +19,16 @@ import ghidra.program.model.data.*; import ghidra.util.InvalidNameException; public class DataTypeNamingUtil { - + private static final String ANONYMOUS_FUNCTION_DEF_PREFIX = "_func"; private DataTypeNamingUtil() { // no construct } - + /** - * Generate a simple mangled function definition name and apply it to the specified functionDefinition. - * Generated name will start with {@code _function_}. + * Generate a simple mangled function definition name and apply it to the specified + * functionDefinition. Generated name will start with {@code _func}. * @param functionDefinition function definition whose name should be set * @return name applied to functionDefinition * @throws IllegalArgumentException if generated name contains unsupported characters 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 64bfc0568c..da6d31ecb8 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 @@ -32,10 +32,11 @@ import ghidra.program.model.symbol.Namespace; */ public class DemangledDataType extends DemangledType { - protected static final String DEMANGLER_ROOT_CATEGORY_PATH = "/Demangler"; + protected static final CategoryPath DEMANGLER_ROOT_CATEGORY_PATH = + CategoryPath.ROOT.extend("Demangler"); protected static final CategoryPath DEMANGLER_ANONYMOUS_FUNCTION_CATEGORY_PATH = - new CategoryPath(DEMANGLER_ROOT_CATEGORY_PATH + "/!_anon_funcs_"); + DEMANGLER_ROOT_CATEGORY_PATH.extend("!_anon_funcs_"); private static final Pattern ARRAY_SUBSCRIPT_PATTERN = Pattern.compile("\\[\\d*\\]"); @@ -157,7 +158,7 @@ public class DemangledDataType extends DemangledType { if (baseType == null || !(baseType instanceof Enum)) { 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 + // Can't tell how big an enum is, just use the size of a pointer dt = new EnumDataType(getDemanglerCategoryPath(getNamespace()), name, dataTypeManager.getDataOrganization().getIntegerSize()); } @@ -338,7 +339,7 @@ public class DemangledDataType extends DemangledType { * Find non-builtin type * @param dataTypeManager data type manager to be searched * @param dtName name of data type - * @param namespace namespace associated with dtName or null if not applicable. If specified, + * @param namespace namespace associated with dtName or null if not applicable. If specified, * a namespace-base category path will be given precedence. * @return data type if found, otherwise null. * @see DataTypeUtilities#findDataType(DataTypeManager, ghidra.program.model.symbol.Namespace, String, Class) for similar namespace @@ -399,18 +400,12 @@ public class DemangledDataType extends DemangledType { return true; } - private static String getNamespacePath(Demangled namespace) { - Demangled ns = namespace; - String namespacePath = ""; - while (ns != null) { - namespacePath = "/" + ns.getName() + namespacePath; - ns = ns.getNamespace(); - } - return namespacePath; - } - + // Recursive method protected static CategoryPath getDemanglerCategoryPath(Demangled namespace) { - return new CategoryPath(DEMANGLER_ROOT_CATEGORY_PATH + getNamespacePath(namespace)); + if (namespace == null) { + return DEMANGLER_ROOT_CATEGORY_PATH; + } + return getDemanglerCategoryPath(namespace.getNamespace()).extend(namespace.getName()); } static Structure createPlaceHolderStructure(String dtName, Demangled namespace) { @@ -697,7 +692,7 @@ public class DemangledDataType extends DemangledType { } } - // the order of __ptr64 and __restrict can vary--with fuzzing... + // the order of __ptr64 and __restrict can vary--with fuzzing... // but what is the natural "real symbol" order? if (isPointer64) { buffer.append(SPACE + PTR64); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CategoryPath.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CategoryPath.java index 058e2efd8b..8e1fe70aaa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CategoryPath.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CategoryPath.java @@ -18,6 +18,7 @@ package ghidra.program.model.data; import java.util.*; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; /** * A category path is the full path to a particular data type @@ -52,7 +53,7 @@ public class CategoryPath implements Comparable { /** * Converts an escaped String suitable for being passed in as a component of a single category - * path string into an non-escaped string. + * path string into an non-escaped string. * @param escapedString String that might need unescaping for characters used for delimiting * @return non-escaped String * @see #escapeString(String) @@ -76,7 +77,8 @@ public class CategoryPath implements Comparable { * * @param parent the parent CategoryPath. Choose {@code ROOT} if needed. * @param subPathElements the array of names of sub-categories of the parent. - * @throws IllegalArgumentException if the given array is null or empty. + * @throws IllegalArgumentException if the parent is null, the elements list is null or empty, + * or an individual element is null */ public CategoryPath(CategoryPath parent, String... subPathElements) { this(parent, Arrays.asList(subPathElements)); @@ -88,15 +90,21 @@ public class CategoryPath implements Comparable { * * @param parent the parent CategoryPath. Choose {@code ROOT} if needed. * @param subPathElements the hierarchical array of sub-categories of the parent. - * @throws IllegalArgumentException if the given list is null or empty. + * @throws IllegalArgumentException if the parent is null, the elements list is null or empty, + * or an individual element is null */ public CategoryPath(CategoryPath parent, List subPathElements) { - Objects.requireNonNull(parent); + if (parent == null) { + throw new IllegalArgumentException("Parent category must not be null!"); + } if (CollectionUtils.isEmpty(subPathElements)) { throw new IllegalArgumentException( "Category list must contain at least one string name!"); } name = subPathElements.get(subPathElements.size() - 1); + if (name == null) { + throw new IllegalArgumentException("A category element must not be null!"); + } if (subPathElements.size() == 1) { this.parent = parent; } @@ -106,6 +114,36 @@ public class CategoryPath implements Comparable { } } + /** + * Returns a CategoryPath that extends the current path using a hierarchical array of strings + * where each string is the name of a category in the category path extension. + * + * @param subPathElements the array of names of sub-categories of the parent. + * @return the extended CategoryPath + * @throws IllegalArgumentException if an element is null + */ + public CategoryPath extend(String... subPathElements) { + if (ArrayUtils.isEmpty(subPathElements)) { + return this; + } + return new CategoryPath(this, subPathElements); + } + + /** + * Returns a CategoryPath that extends the current path using a hierarchical list of strings + * where each string is the name of a category in the category path extension. + * + * @param subPathElements the hierarchical array of sub-categories of the parent. + * @return the extended CategoryPath + * @throws IllegalArgumentException if an element is null + */ + public CategoryPath extend(List subPathElements) { + if (CollectionUtils.isEmpty(subPathElements)) { + return this; + } + return new CategoryPath(this, subPathElements); + } + /** * Creates a category path given a forward-slash-delimited string (e.g., {@code "/aa/bb"}). * If an individual path component has one or more '/' characters in it, then it can be @@ -121,7 +159,7 @@ public class CategoryPath implements Comparable { */ // NOTE: We purposefully did not create a constructor that takes varags only, as that // constructor, called with a single argument that would not be escaped, would conflict with - // this constructor, which requires an escaped argument. + // this constructor, which requires an escaped argument. public CategoryPath(String path) { if (path == null || path.length() == 0 || path.equals(DELIMITER_STRING)) { // parent can only be null for ROOT diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java index 852aaa87a2..b23782558b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java @@ -116,6 +116,20 @@ public class CategoryPathTest extends AbstractGTest { assertEquals("mango", c.getName()); } + @Test(expected = IllegalArgumentException.class) + public void testConstructorNullParent() { + List list = new ArrayList<>(); + list.add("element"); + new CategoryPath(null, list); + } + + @Test(expected = IllegalArgumentException.class) + public void testConstructorNullElement() { + List list = new ArrayList<>(); + list.add(null); + new CategoryPath(CategoryPath.ROOT, list); + } + @Test public void testConstructorParentAndList() { CategoryPath parent = new CategoryPath("/universe/earth"); @@ -159,6 +173,49 @@ public class CategoryPathTest extends AbstractGTest { assertTrue(c.isRoot()); } + @Test + public void testExtendList() { + CategoryPath parent = new CategoryPath("/universe/earth"); + List list = new ArrayList<>(); + list.add("boy"); + list.add("bad"); + CategoryPath c = parent.extend(list); + assertEquals("/universe/earth/boy/bad", c.getPath()); + assertEquals("bad", c.getName()); + } + + @Test + public void testExtendVarargsArray() { + CategoryPath parent = new CategoryPath("/apple/peaches"); + CategoryPath c = parent.extend(new String[] { "pumpkin", "pie" }); + assertEquals("pie", c.getName()); + c = c.getParent(); + assertEquals("pumpkin", c.getName()); + c = c.getParent(); + assertEquals("peaches", c.getName()); + c = c.getParent(); + assertEquals("apple", c.getName()); + c = c.getParent(); + assertEquals("", c.getName()); + assertTrue(c.isRoot()); + } + + @Test + public void testExtendVarargs() { + CategoryPath parent = new CategoryPath("/apple/peaches"); + CategoryPath c = parent.extend("pumpkin", "pie"); + assertEquals("pie", c.getName()); + c = c.getParent(); + assertEquals("pumpkin", c.getName()); + c = c.getParent(); + assertEquals("peaches", c.getName()); + c = c.getParent(); + assertEquals("apple", c.getName()); + c = c.getParent(); + assertEquals("", c.getName()); + assertTrue(c.isRoot()); + } + @Test(expected = IllegalArgumentException.class) public void testConstructorBadCtorParam_empty_path_element() { new CategoryPath("//");