From d2a0235fdd00adde85a81c6362b12dfd4cc551b8 Mon Sep 17 00:00:00 2001 From: astrelsky Date: Fri, 26 Jun 2020 19:18:13 -0400 Subject: [PATCH 1/2] Removed DWARF COLON_MANGLE --- .../app/util/bin/format/dwarf4/next/NamespacePath.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java index 614b790db3..8e8795c547 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java @@ -59,12 +59,9 @@ public class NamespacePath implements Comparable { } private static final String FWDSLASH_MANGLE = "-fwdslash-"; - private static final String COLON_MANGLE = "-"; private static String preMangleName(String name) { - return name == null ? null - : name.replaceAll(":", COLON_MANGLE).replaceAll(" ", "").replaceAll("/", - FWDSLASH_MANGLE); + return name == null ? null : name.replaceAll(" ", "").replaceAll("/", FWDSLASH_MANGLE); } private final NamespacePath parent; From 8a407a1169a75c628d146cdfcc64747ecb3f90ba Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Mon, 12 Jul 2021 16:44:19 -0400 Subject: [PATCH 2/2] GP-1122 (PR #2043) remove ':', '/' mangling and change ' ' mangling Change DWARF mangling logic to allow ':', '/' and convert ' ' to '_'. Fixup some spots that were using fragile CategoryPath / Namespace string parsing. --- .../CreateEnumFromSelectionAction.java | 2 +- .../dwarf4/next/DWARFFunctionImporter.java | 2 +- .../bin/format/dwarf4/next/DWARFNameInfo.java | 113 +++++++++++++++++- .../bin/format/dwarf4/next/DWARFParser.java | 20 +--- .../bin/format/dwarf4/next/DWARFProgram.java | 11 +- .../bin/format/dwarf4/next/NamespacePath.java | 37 +++--- .../next/DWARFDataTypeImporterTest.java | 65 ++++++++++ .../next/DWARFFunctionImporterTest.java | 50 +++++++- .../format/dwarf4/next/DWARFNameInfoTest.java | 100 ++++++++++++---- .../bin/format/dwarf4/NamespacePathTest.java | 11 +- .../java/ghidra/app/util/NamespaceUtils.java | 18 +++ .../database/data/DataTypeUtilities.java | 23 ++-- .../program/model/symbol/SymbolUtilities.java | 2 +- 13 files changed, 354 insertions(+), 100 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java index 84799c9945..11da0db296 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java @@ -81,7 +81,7 @@ public class CreateEnumFromSelectionAction extends DockingAction { for (DataTypeManager dataTypeManager : dataTypeManagers) { if (dataTypeManager instanceof ProgramDataTypeManager) { myDataTypeManager = dataTypeManager; - category = myDataTypeManager.getCategory(new CategoryPath("/")); + category = myDataTypeManager.getCategory(CategoryPath.ROOT); if (category == null) { Msg.error(this, "Could not find program data type manager"); return; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java index 6daf5a17e9..421ef74c2a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java @@ -689,7 +689,7 @@ public class DWARFFunctionImporter { DWARFNameInfo dni = prog.getName(diea); - String name = SymbolUtilities.replaceInvalidChars(dni.getName(), false); + String name = dni.getName(); Number lowPC = null; boolean disjoint = false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java index c56109c438..c79e199844 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfo.java @@ -16,8 +16,8 @@ package ghidra.app.util.bin.format.dwarf4.next; import java.util.List; +import java.util.Objects; -import ghidra.formats.gfilesystem.FSUtilities; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.Namespace; @@ -35,15 +35,38 @@ public class DWARFNameInfo { private final NamespacePath namespacePath; private final String originalName; + /** + * Create a root name entry that will serve as the parent for all children. + * + * @param rootCategory {@link CategoryPath} in the data type manager that will contain + * any sub-categories that represent namespaces + * @return a new {@link DWARFNameInfo} instance + */ public static DWARFNameInfo createRoot(CategoryPath rootCategory) { return new DWARFNameInfo(null, rootCategory, NamespacePath.ROOT, null); } + /** + * Create a {@link DWARFNameInfo} instance using the specified {@link DataType}'s name. + * + * @param dataType {@link DataType} + * @return new {@link DWARFNameInfo} using the same name / CategoryPath as the data type + */ public static DWARFNameInfo fromDataType(DataType dataType) { return new DWARFNameInfo(null, dataType.getCategoryPath(), NamespacePath.create(null, dataType.getName(), null), dataType.getName()); } + /** + * Create a child {@link DWARFNameInfo} instance of the specified parent. + *

+ * Example:
+ *

fromList(parent, List.of("name1", "name2")) → parent_name/name1/name2
+ * + * @param parent {@link DWARFNameInfo} parent + * @param names list of names + * @return new {@link DWARFNameInfo} instance that is a child of the parent + */ public static DWARFNameInfo fromList(DWARFNameInfo parent, List names) { for (String s : names) { DWARFNameInfo tmp = new DWARFNameInfo(parent, s, s, SymbolType.NAMESPACE); @@ -56,8 +79,8 @@ public class DWARFNameInfo { NamespacePath namespacePath, String originalName) { this.parent = parent; this.organizationalCategoryPath = - (organizationalCategoryPath != null) ? organizationalCategoryPath : CategoryPath.ROOT; - this.namespacePath = (namespacePath != null) ? namespacePath : NamespacePath.ROOT; + Objects.requireNonNullElse(organizationalCategoryPath, CategoryPath.ROOT); + this.namespacePath = Objects.requireNonNullElse(namespacePath, NamespacePath.ROOT); this.originalName = originalName; } @@ -68,38 +91,89 @@ public class DWARFNameInfo { this.originalName = originalName; } + /** + * Returns the parent name + * + * @return parent + */ public DWARFNameInfo getParent() { return parent; } + /** + * Returns true if this instance has no parent and is considered the root. + * + * @return boolean true if root name, false if not root + */ public boolean isRoot() { return parent == null; } + /** + * Returns the organizational category path. + * + * @return organizational category path for dwarf names + */ public CategoryPath getOrganizationalCategoryPath() { return organizationalCategoryPath; } + /** + * Returns the NamespacePath of this instance. + * + * @return {@link NamespacePath} of this instance + */ public NamespacePath getNamespacePath() { return namespacePath; } + /** + * Returns the parent's CategoryPath. + * + * @return parent name's CategoryPath + */ public CategoryPath getParentCP() { return getParent().asCategoryPath(); } + /** + * Returns the name of this entry. + * + * @return string name of this entry, safe to use to name a Ghidra object (datatype, namespace, + * etc) + */ public String getName() { return namespacePath.getName(); } + /** + * Creates a new DWARFNameInfo instance, using this instance as the template, replacing + * the name with a new name. + * + * @param newName name for the new instance + * @param newOriginalName originalName for the new instance + * @return new instance with new name + */ public DWARFNameInfo replaceName(String newName, String newOriginalName) { return new DWARFNameInfo(getParent(), newOriginalName, newName, getType()); } + /** + * Creates a new DWARFNameInfo instance, using this instance as the template, replacing + * the SymbolType with a new value. + * + * @param newType new SymbolType value + * @return new instance with the specified SymbolType + */ public DWARFNameInfo replaceType(SymbolType newType) { return new DWARFNameInfo(parent, originalName, getName(), newType); } + /** + * Returns the SymbolType of this name. + * + * @return {@link SymbolType} of this entry + */ public SymbolType getType() { return namespacePath.getType(); } @@ -110,8 +184,10 @@ public class DWARFNameInfo { * @return {@link CategoryPath}: "/organizational_cat_path/namespace1/namespace2/obj_name" */ public CategoryPath asCategoryPath() { - return new CategoryPath(FSUtilities.appendPath(organizationalCategoryPath.getPath(), - namespacePath.isRoot() ? null : namespacePath.asCategoryPathString())); + List nsParts = namespacePath.getParts(); + return nsParts.isEmpty() + ? organizationalCategoryPath + : new CategoryPath(organizationalCategoryPath, nsParts); } /** @@ -123,6 +199,12 @@ public class DWARFNameInfo { return !isRoot() ? new DataTypePath(getParentCP(), getName()) : null; } + /** + * Returns the Ghidra {@link Namespace} that represents this entry's parent. + * + * @param program the Ghidra program that contains the namespace + * @return {@link Namespace} representing this entry's parent + */ public Namespace getParentNamespace(Program program) { return getParent().asNamespace(program); } @@ -143,10 +225,20 @@ public class DWARFNameInfo { return organizationalCategoryPath.toString() + " || " + namespacePath.toString(); } + /** + * Returns true if the original name of this entry was blank. + * + * @return boolean true if there was no original name + */ public boolean isAnon() { return originalName == null; } + /** + * Returns the original name (unmodified by Ghidra-isms) of this entry. + * + * @return original name + */ public String getOriginalName() { return originalName; } @@ -156,12 +248,21 @@ public class DWARFNameInfo { * than its {@link #getOriginalName() original} form. *

* - * @return + * @return boolean true if the original name doesn't match the ghidra-ized name */ public boolean isNameModified() { return originalName == null || !originalName.equals(namespacePath.getName()); } + /** + * Creates a {@link DWARFNameInfo} instance, which has a name that is contained with + * this instance's namespace, using the specified name and symbol type. + * + * @param childOriginalName the unmodified name + * @param childName the ghidra-ized name of the type/symbol/namespace/etc + * @param childType the type of the object being named + * @return new DWARFNameInfo instance + */ public DWARFNameInfo createChild(String childOriginalName, String childName, SymbolType childType) { return new DWARFNameInfo(this, childOriginalName, childName, childType); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java index 878e5b5746..35686c66a6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java @@ -170,21 +170,13 @@ public class DWARFParser { if (origRoot.equals(cp)) { return newRoot; } - - String origRootPath = origRoot.getPath(); - if (!CategoryPath.ROOT.equals(origRoot)) { - origRootPath += "/"; + List origRootParts = origRoot.asList(); + List cpParts = cp.asList(); + if (cpParts.size() < origRootParts.size() || + !origRootParts.equals(cpParts.subList(0, origRootParts.size()))) { + return null; } - String newRootPath = newRoot.getPath(); - if (!CategoryPath.ROOT.equals(newRoot)) { - newRootPath += "/"; - } - String cpPath = cp.getPath(); - if (cpPath.startsWith(origRootPath)) { - String newPath = newRootPath + cpPath.substring(origRootPath.length()); - return new CategoryPath(newPath); - } - return null; + return new CategoryPath(newRoot, cpParts.subList(origRootParts.size(), cpParts.size())); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java index 97f68fc3d1..ab27c53490 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java @@ -15,10 +15,9 @@ */ package ghidra.app.util.bin.format.dwarf4.next; -import java.util.*; - import java.io.Closeable; import java.io.IOException; +import java.util.*; import org.apache.commons.collections4.ListValuedMap; import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; @@ -396,14 +395,6 @@ public class DWARFProgram implements Closeable { String origName = isAnon ? null : name; String workingName = ensureSafeNameLength(name); - switch (diea.getTag()) { - // fixup DWARF entries that are related to Ghidra symbols - case DWARFTag.DW_TAG_subroutine_type: - case DWARFTag.DW_TAG_subprogram: - case DWARFTag.DW_TAG_inlined_subroutine: - workingName = SymbolUtilities.replaceInvalidChars(workingName, false); - break; - } DWARFNameInfo result = parentDNI.createChild(origName, workingName, DWARFUtil.getSymbolTypeFromDIE(diea)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java index 8e8795c547..1b4a6e9679 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/NamespacePath.java @@ -15,10 +15,10 @@ */ package ghidra.app.util.bin.format.dwarf4.next; +import java.util.*; import java.util.function.Consumer; import ghidra.app.util.NamespaceUtils; -import ghidra.program.model.data.CategoryPath; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.*; import ghidra.util.Msg; @@ -55,13 +55,8 @@ public class NamespacePath implements Comparable { * @return new {@link NamespacePath} */ public static NamespacePath create(NamespacePath parent, String name, SymbolType type) { - return new NamespacePath(parent == null ? ROOT : parent, preMangleName(name), type); - } - - private static final String FWDSLASH_MANGLE = "-fwdslash-"; - - private static String preMangleName(String name) { - return name == null ? null : name.replaceAll(" ", "").replaceAll("/", FWDSLASH_MANGLE); + return new NamespacePath(Objects.requireNonNullElse(parent, ROOT), + SymbolUtilities.replaceInvalidChars(name, true), type); } private final NamespacePath parent; @@ -171,17 +166,6 @@ public class NamespacePath implements Comparable { } } - /** - * Converts this namespace path into a {@link CategoryPath} style string. - * @return string path "/namespace1/namespace2" - */ - public String asCategoryPathString() { - StringBuilder sb = new StringBuilder(); - doInOrderTraversal( - nsp -> sb.append(sb.length() != 1 ? "/" : "").append(nsp.isRoot() ? "" : nsp.name)); - return sb.toString(); - } - /** * Converts this namespace path into a {@link Namespace} style string. * @return string path "ROOT::namespace1::namespace2" @@ -212,6 +196,21 @@ public class NamespacePath implements Comparable { return sb.toString(); } + /** + * Returns the individual parts of the path as elements in a list. + * + * @return list of strings containing individual parts of the path + */ + public List getParts() { + List partList = new ArrayList<>(); + doInOrderTraversal(nsp -> { + if (!nsp.isRoot()) { + partList.add(nsp.name); + } + }); + return partList; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java index e82e702db5..677ed4450a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java @@ -894,6 +894,71 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { return results; } + @Test + public void testStructNamespaceReservedChar_Colon() + throws CancelledException, IOException, DWARFException { + DebugInfoEntry intDIE = addInt(cu); + DebugInfoEntry floatDIE = addFloat(cu); + + //----------------------- + DebugInfoEntry struct1DIE = newStruct("mystruct::with::colons", 100).create(cu); + DebugInfoEntry nestedStructDIE = + newStruct("nested_struct", 10).setParent(struct1DIE).create(cu); + newMember(nestedStructDIE, "blah1", intDIE, 0).create(cu); + newMember(struct1DIE, "f1", intDIE, 0).create(cu); + newMember(struct1DIE, "f2", floatDIE, 10).create(cu); + newMember(struct1DIE, "f3", nestedStructDIE, 20).create(cu); + //---------------------- + + importAllDataTypes(); + + Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct::with::colons"); + assertEquals(3, structdt.getNumDefinedComponents()); + Structure nestedStructDt = (Structure) structdt.getDefinedComponents()[2].getDataType(); + assertEquals("mystruct::with::colons", nestedStructDt.getCategoryPath().getName()); + } + + @Test + public void testStructNamespaceReservedChar_FwdSlash() + throws CancelledException, IOException, DWARFException { + DebugInfoEntry intDIE = addInt(cu); + DebugInfoEntry floatDIE = addFloat(cu); + + //----------------------- + DebugInfoEntry struct1DIE = newStruct("mystruct::operator/()", 100).create(cu); + DebugInfoEntry nestedStructDIE = + newStruct("nested_struct", 10).setParent(struct1DIE).create(cu); + newMember(nestedStructDIE, "blah1", intDIE, 0).create(cu); + newMember(struct1DIE, "f1", intDIE, 0).create(cu); + newMember(struct1DIE, "f2", floatDIE, 10).create(cu); + newMember(struct1DIE, "f3", nestedStructDIE, 20).create(cu); + //---------------------- + + importAllDataTypes(); + + Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct::operator/()"); + assertEquals(3, structdt.getNumDefinedComponents()); + Structure nestedStructDt = (Structure) structdt.getDefinedComponents()[2].getDataType(); + assertEquals("mystruct::operator/()", nestedStructDt.getCategoryPath().getName()); + } + + @Test + public void testStructNamespaceReservedChar_Spaces() + throws CancelledException, IOException, DWARFException { + DebugInfoEntry intDIE = addInt(cu); + DebugInfoEntry floatDIE = addFloat(cu); + + //----------------------- + DebugInfoEntry struct1DIE = newStruct("mystruct", 100).create(cu); + newMember(struct1DIE, "f1", intDIE, 0).create(cu); + newMember(struct1DIE, "f2", floatDIE, 10).create(cu); + //---------------------- + + importAllDataTypes(); + + Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); + assertEquals(2, structdt.getNumDefinedComponents()); + } //---------------------------------------------------------------------------------------------------- @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java index 57f70dd740..8802a76594 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporterTest.java @@ -18,15 +18,17 @@ package ghidra.app.util.bin.format.dwarf4.next; import static org.junit.Assert.*; import java.io.IOException; +import java.util.List; import org.junit.Test; +import ghidra.app.util.NamespaceUtils; import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes; -import ghidra.program.model.data.DataType; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.Parameter; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.Namespace; import ghidra.util.exception.CancelledException; public class DWARFFunctionImporterTest extends DWARFTestBase { @@ -113,4 +115,46 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { assertNotNull(returnType); assertEquals("int", returnType.getName()); } + + @Test + public void testNamespace_with_reserved_chars() + throws CancelledException, IOException, DWARFException { + // simulate what happens when a C++ operator/ or a templated classname + // that contains a namespaced template argument (templateclass) + // is encountered and a Ghidra namespace is created + + DebugInfoEntry intDIE = addInt(cu); + DebugInfoEntry floatDIE = addFloat(cu); + DebugInfoEntry struct1DIE = newStruct("mystruct::operator/()", 100).create(cu); + DebugInfoEntry nestedStructPtrDIE = addFwdPtr(cu, 1); + DebugInfoEntry nestedStructDIE = + newStruct("nested_struct", 10).setParent(struct1DIE).create(cu); + newMember(nestedStructDIE, "blah1", intDIE, 0).create(cu); + DebugInfoEntry fooDIE = + newSubprogram("foo", intDIE, 0x410, 10).setParent(nestedStructDIE).create(cu); + newFormalParam(fooDIE, "this", nestedStructPtrDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) + .create(cu); + + newMember(struct1DIE, "f1", intDIE, 0).create(cu); + newMember(struct1DIE, "f2", floatDIE, 10).create(cu); + newMember(struct1DIE, "f3", nestedStructDIE, 20).create(cu); + + importFunctions(); + + Function fooFunc = program.getListing().getFunctionAt(addr(0x410)); + List nsParts = NamespaceUtils.getNameSpaceParts(fooFunc.getParentNamespace()); + + assertEquals("foo", fooFunc.getName()); + assertEquals("nested_struct", + ((Pointer) fooFunc.getParameter(0).getDataType()).getDataType().getName()); + assertEquals("__thiscall", fooFunc.getCallingConventionName()); + assertEquals("nested_struct", nsParts.get(nsParts.size() - 1).getName()); + assertEquals("mystruct::operator/()", nsParts.get(nsParts.size() - 2).getName()); + + // Test that VariableUtilities can find the structure for the this* pointer + Structure nestedStructDT1 = (Structure) dataMgr + .getDataType(new CategoryPath(rootCP, "mystruct::operator/()"), "nested_struct"); + Structure nestedStructDT2 = VariableUtilities.findExistingClassStruct(fooFunc); + assertTrue(nestedStructDT1 == nestedStructDT2); + } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java index 3a8d95da3a..fe0623e93e 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFNameInfoTest.java @@ -15,15 +15,16 @@ */ package ghidra.app.util.bin.format.dwarf4.next; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; -import org.junit.Assert; import org.junit.Test; import ghidra.app.util.bin.format.dwarf4.*; import ghidra.program.model.data.*; +import ghidra.program.model.listing.GhidraClass; +import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.SymbolType; import ghidra.util.exception.CancelledException; @@ -35,8 +36,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testSimple() { DWARFNameInfo dni = root.createChild("simplename", "simplename", SymbolType.CLASS); - Assert.assertEquals(new CategoryPath("/TEST_DNI/simplename"), dni.asCategoryPath()); - Assert.assertEquals( + assertEquals(new CategoryPath("/TEST_DNI/simplename"), dni.asCategoryPath()); + assertEquals( NamespacePath.create(NamespacePath.ROOT, "simplename", SymbolType.CLASS), dni.getNamespacePath()); } @@ -44,12 +45,69 @@ public class DWARFNameInfoTest extends DWARFTestBase { @Test public void testSlash() { DWARFNameInfo dni = root.createChild("blah/", "blah/", SymbolType.CLASS); - Assert.assertEquals(new CategoryPath("/TEST_DNI/blah-fwdslash-"), dni.asCategoryPath()); - Assert.assertEquals( - NamespacePath.create(NamespacePath.ROOT, "blah-fwdslash-", SymbolType.CLASS), + assertEquals(new CategoryPath(CategoryPath.ROOT, "TEST_DNI", "blah/"), + dni.asCategoryPath()); + + Namespace testNamespace = dni.asNamespace(program); + assertEquals("blah/", testNamespace.getName(true)); + assertEquals( + NamespacePath.create(NamespacePath.ROOT, "blah/", SymbolType.CLASS), dni.getNamespacePath()); } + @Test + public void testColon() { + DWARFNameInfo dni = root.createChild("blah::blah", "blah::blah", SymbolType.CLASS); + DWARFNameInfo dni2 = dni.createChild("sub::sub", "sub::sub", SymbolType.CLASS); + assertEquals(new CategoryPath("/TEST_DNI/blah::blah/sub::sub"), + dni2.asCategoryPath()); + + Namespace testNamespace = dni2.asNamespace(program); + assertEquals("sub::sub", testNamespace.getName()); + assertEquals("blah::blah", testNamespace.getParentNamespace().getName()); + assertTrue(testNamespace instanceof GhidraClass); + assertTrue(testNamespace.getParentNamespace() instanceof GhidraClass); + } + + @Test + public void testNamespaceTypes() { + DWARFNameInfo dni_ns1 = root.createChild("ns1", "ns1", SymbolType.NAMESPACE); + DWARFNameInfo dni_ns1class1 = dni_ns1.createChild("class1", "class1", SymbolType.CLASS); + assertEquals(new CategoryPath("/TEST_DNI/ns1/class1"), dni_ns1class1.asCategoryPath()); + + Namespace class1_ns = dni_ns1class1.asNamespace(program); + assertEquals("ns1::class1", class1_ns.getName(true)); + assertTrue(class1_ns instanceof GhidraClass); + assertFalse(class1_ns.getParentNamespace() instanceof GhidraClass); + } + + @Test + public void testNamespaceConvertToClass() { + // tests that a plain namespace can be 'upgraded' to a class namespace + DWARFNameInfo dni_ns1 = root.createChild("ns1", "ns1", SymbolType.NAMESPACE); + + Namespace ns1 = dni_ns1.asNamespace(program); + assertFalse(ns1 instanceof GhidraClass); + + DWARFNameInfo dni_ns1a = root.createChild("ns1", "ns1", SymbolType.CLASS); + Namespace ns1a = dni_ns1a.asNamespace(program); + assertTrue(ns1a instanceof GhidraClass); + } + + @Test + public void testClassConvertToNamespace() { + // tests that a class namespace isn't 'downgraded' to a plain namespace + DWARFNameInfo dni_class1 = root.createChild("class1", "class1", SymbolType.CLASS); + + Namespace class1 = dni_class1.asNamespace(program); + assertTrue(class1 instanceof GhidraClass); + + DWARFNameInfo dni_class1a = root.createChild("class1", "class1", SymbolType.NAMESPACE); + Namespace class1a = dni_class1a.asNamespace(program); + // symbol should NOT have been converted to a plain namespace. + assertTrue(class1a instanceof GhidraClass); + } + @Test public void testNestedStructNames() throws CancelledException, IOException, DWARFException { DebugInfoEntry structDIE = newStruct("struct", 100).create(cu); @@ -60,8 +118,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); - Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); - Assert.assertEquals(rootCP.getPath() + "/struct/substruct", substructDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct/substruct", substructDT.getPathName()); } @Test @@ -75,8 +133,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); - Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); - Assert.assertEquals(rootCP.getPath() + "/struct/anon_struct_0", substructDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct/anon_struct_0", substructDT.getPathName()); } @Test @@ -91,8 +149,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); - Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); - Assert.assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1", + assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1", substructDT.getPathName()); } @@ -109,8 +167,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); - Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); - Assert.assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1_f2", + assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1_f2", substructDT.getPathName()); } @@ -125,12 +183,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { importAllDataTypes(); Structure structDT = (Structure) dwarfDTM.getDataType(structDIE.getOffset(), null); - DataTypeComponent f1 = structDT.getComponentAt(0); - DataTypeComponent f2 = structDT.getComponentAt(20); - System.out.println(f1.getDataType()); - System.out.println(f2.getDataType()); - Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); } @Test @@ -143,12 +197,8 @@ public class DWARFNameInfoTest extends DWARFTestBase { importAllDataTypes(); Structure structDT = (Structure) dwarfDTM.getDataType(structDIE.getOffset(), null); - DataTypeComponent f1 = structDT.getComponentAt(0); - DataTypeComponent f2 = structDT.getComponentAt(20); - System.out.println(f1.getDataType()); - System.out.println(f2.getDataType()); - Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); + assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); } @Test diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java index ca4cc16ef6..dd21bfabb3 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/NamespacePathTest.java @@ -30,9 +30,6 @@ public class NamespacePathTest { Assert.assertEquals("ROOT::sub1", nsp.asNamespaceString()); Assert.assertEquals("ROOT::sub1::sub1_1", nsp1_1.asNamespaceString()); - Assert.assertEquals("/sub1", nsp.asCategoryPathString()); - Assert.assertEquals("/sub1/sub1_1", nsp1_1.asCategoryPathString()); - } @Test @@ -41,8 +38,12 @@ public class NamespacePathTest { NamespacePath nsSpaceA = NamespacePath.create(null, "ns A", SymbolType.NAMESPACE); NamespacePath nsColonA = NamespacePath.create(null, "ns:A", SymbolType.NAMESPACE); - Assert.assertEquals("ROOT::ns-fwdslash-A", nsSlashA.asNamespaceString()); + Assert.assertEquals("ROOT::ns/A", nsSlashA.asNamespaceString()); Assert.assertEquals("ROOT::nsA", nsSpaceA.asNamespaceString()); - Assert.assertEquals("ROOT::ns-A", nsColonA.asNamespaceString()); + Assert.assertEquals("ROOT::ns:A", nsColonA.asNamespaceString()); + + Assert.assertEquals("ns/A", nsSlashA.getName()); + Assert.assertEquals("nsA", nsSpaceA.getName()); + Assert.assertEquals("ns:A", nsColonA.getName()); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java index 94e7613e0e..5256050e38 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/NamespaceUtils.java @@ -537,4 +537,22 @@ public class NamespaceUtils { SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable(); return symbolTable.convertNamespaceToClass(namespace); } + + /** + * Returns a list of namespaces, where each element is a component of the original specified + * namespace, excluding the global root namespace. + *

+ * Namespace "ns1::ns2::ns3" returns [ "ns1", "ns1::ns2", "ns1::ns2::ns3" ] + * + * @param namespace namespace to process + * @return list of namespaces + */ + public static List getNameSpaceParts(Namespace namespace) { + List result = new ArrayList<>(); + while (!namespace.isGlobal()) { + result.add(0, namespace); + namespace = namespace.getParentNamespace(); + } + return result; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java index 3d72ba76c7..b9546e2895 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.util.*; import java.util.regex.Pattern; +import ghidra.app.util.NamespaceUtils; import ghidra.docking.settings.Settings; import ghidra.program.model.address.GlobalNamespace; import ghidra.program.model.data.*; @@ -341,23 +342,15 @@ public class DataTypeUtilities { */ public static CategoryPath getDataTypeCategoryPath(CategoryPath baseCategory, Namespace namespace) { - Namespace ns = namespace; - String path = ""; - while (!ns.isGlobal() && !(ns instanceof Library)) { - if (path.length() != 0) { - path = "/" + path; + List categoryPathParts = new ArrayList<>(); + for (Namespace ns : NamespaceUtils.getNameSpaceParts(namespace)) { + if (!(ns instanceof Library)) { + categoryPathParts.add(ns.getName()); } - path = ns.getName() + path; - ns = ns.getParentNamespace(); } - if (path.length() == 0) { - return baseCategory; - } - String categoryPath = CategoryPath.DELIMITER_CHAR + path; - if (!baseCategory.equals(CategoryPath.ROOT)) { - categoryPath = baseCategory.getPath() + categoryPath; - } - return new CategoryPath(categoryPath); + return categoryPathParts.isEmpty() + ? baseCategory + : new CategoryPath(baseCategory, categoryPathParts); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolUtilities.java index dbbf836370..21305c61d6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/SymbolUtilities.java @@ -364,7 +364,7 @@ public class SymbolUtilities { return null; } int len = str.length(); - StringBuffer buf = new StringBuffer(len); + StringBuilder buf = new StringBuilder(len); for (int i = 0; i < len; ++i) { char c = str.charAt(i); if (isInvalidChar(c)) {