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] 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("//");