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.
This commit is contained in:
dev747368 2021-07-12 16:44:19 -04:00
parent d2a0235fdd
commit 8a407a1169
13 changed files with 354 additions and 100 deletions

View file

@ -81,7 +81,7 @@ public class CreateEnumFromSelectionAction extends DockingAction {
for (DataTypeManager dataTypeManager : dataTypeManagers) { for (DataTypeManager dataTypeManager : dataTypeManagers) {
if (dataTypeManager instanceof ProgramDataTypeManager) { if (dataTypeManager instanceof ProgramDataTypeManager) {
myDataTypeManager = dataTypeManager; myDataTypeManager = dataTypeManager;
category = myDataTypeManager.getCategory(new CategoryPath("/")); category = myDataTypeManager.getCategory(CategoryPath.ROOT);
if (category == null) { if (category == null) {
Msg.error(this, "Could not find program data type manager"); Msg.error(this, "Could not find program data type manager");
return; return;

View file

@ -689,7 +689,7 @@ public class DWARFFunctionImporter {
DWARFNameInfo dni = prog.getName(diea); DWARFNameInfo dni = prog.getName(diea);
String name = SymbolUtilities.replaceInvalidChars(dni.getName(), false); String name = dni.getName();
Number lowPC = null; Number lowPC = null;
boolean disjoint = false; boolean disjoint = false;

View file

@ -16,8 +16,8 @@
package ghidra.app.util.bin.format.dwarf4.next; package ghidra.app.util.bin.format.dwarf4.next;
import java.util.List; import java.util.List;
import java.util.Objects;
import ghidra.formats.gfilesystem.FSUtilities;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
@ -35,15 +35,38 @@ public class DWARFNameInfo {
private final NamespacePath namespacePath; private final NamespacePath namespacePath;
private final String originalName; 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) { public static DWARFNameInfo createRoot(CategoryPath rootCategory) {
return new DWARFNameInfo(null, rootCategory, NamespacePath.ROOT, null); 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) { public static DWARFNameInfo fromDataType(DataType dataType) {
return new DWARFNameInfo(null, dataType.getCategoryPath(), return new DWARFNameInfo(null, dataType.getCategoryPath(),
NamespacePath.create(null, dataType.getName(), null), dataType.getName()); NamespacePath.create(null, dataType.getName(), null), dataType.getName());
} }
/**
* Create a child {@link DWARFNameInfo} instance of the specified parent.
* <p>
* Example:<br>
* <pre>fromList(parent, List.of("name1", "name2")) &rarr; parent_name/name1/name2</pre>
*
* @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<String> names) { public static DWARFNameInfo fromList(DWARFNameInfo parent, List<String> names) {
for (String s : names) { for (String s : names) {
DWARFNameInfo tmp = new DWARFNameInfo(parent, s, s, SymbolType.NAMESPACE); DWARFNameInfo tmp = new DWARFNameInfo(parent, s, s, SymbolType.NAMESPACE);
@ -56,8 +79,8 @@ public class DWARFNameInfo {
NamespacePath namespacePath, String originalName) { NamespacePath namespacePath, String originalName) {
this.parent = parent; this.parent = parent;
this.organizationalCategoryPath = this.organizationalCategoryPath =
(organizationalCategoryPath != null) ? organizationalCategoryPath : CategoryPath.ROOT; Objects.requireNonNullElse(organizationalCategoryPath, CategoryPath.ROOT);
this.namespacePath = (namespacePath != null) ? namespacePath : NamespacePath.ROOT; this.namespacePath = Objects.requireNonNullElse(namespacePath, NamespacePath.ROOT);
this.originalName = originalName; this.originalName = originalName;
} }
@ -68,38 +91,89 @@ public class DWARFNameInfo {
this.originalName = originalName; this.originalName = originalName;
} }
/**
* Returns the parent name
*
* @return parent
*/
public DWARFNameInfo getParent() { public DWARFNameInfo getParent() {
return parent; 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() { public boolean isRoot() {
return parent == null; return parent == null;
} }
/**
* Returns the organizational category path.
*
* @return organizational category path for dwarf names
*/
public CategoryPath getOrganizationalCategoryPath() { public CategoryPath getOrganizationalCategoryPath() {
return organizationalCategoryPath; return organizationalCategoryPath;
} }
/**
* Returns the NamespacePath of this instance.
*
* @return {@link NamespacePath} of this instance
*/
public NamespacePath getNamespacePath() { public NamespacePath getNamespacePath() {
return namespacePath; return namespacePath;
} }
/**
* Returns the parent's CategoryPath.
*
* @return parent name's CategoryPath
*/
public CategoryPath getParentCP() { public CategoryPath getParentCP() {
return getParent().asCategoryPath(); 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() { public String getName() {
return namespacePath.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) { public DWARFNameInfo replaceName(String newName, String newOriginalName) {
return new DWARFNameInfo(getParent(), newOriginalName, newName, getType()); 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) { public DWARFNameInfo replaceType(SymbolType newType) {
return new DWARFNameInfo(parent, originalName, getName(), newType); return new DWARFNameInfo(parent, originalName, getName(), newType);
} }
/**
* Returns the SymbolType of this name.
*
* @return {@link SymbolType} of this entry
*/
public SymbolType getType() { public SymbolType getType() {
return namespacePath.getType(); return namespacePath.getType();
} }
@ -110,8 +184,10 @@ public class DWARFNameInfo {
* @return {@link CategoryPath}: "/organizational_cat_path/namespace1/namespace2/obj_name" * @return {@link CategoryPath}: "/organizational_cat_path/namespace1/namespace2/obj_name"
*/ */
public CategoryPath asCategoryPath() { public CategoryPath asCategoryPath() {
return new CategoryPath(FSUtilities.appendPath(organizationalCategoryPath.getPath(), List<String> nsParts = namespacePath.getParts();
namespacePath.isRoot() ? null : namespacePath.asCategoryPathString())); return nsParts.isEmpty()
? organizationalCategoryPath
: new CategoryPath(organizationalCategoryPath, nsParts);
} }
/** /**
@ -123,6 +199,12 @@ public class DWARFNameInfo {
return !isRoot() ? new DataTypePath(getParentCP(), getName()) : null; 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) { public Namespace getParentNamespace(Program program) {
return getParent().asNamespace(program); return getParent().asNamespace(program);
} }
@ -143,10 +225,20 @@ public class DWARFNameInfo {
return organizationalCategoryPath.toString() + " || " + namespacePath.toString(); 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() { public boolean isAnon() {
return originalName == null; return originalName == null;
} }
/**
* Returns the original name (unmodified by Ghidra-isms) of this entry.
*
* @return original name
*/
public String getOriginalName() { public String getOriginalName() {
return originalName; return originalName;
} }
@ -156,12 +248,21 @@ public class DWARFNameInfo {
* than its {@link #getOriginalName() original} form. * than its {@link #getOriginalName() original} form.
* <p> * <p>
* *
* @return * @return boolean true if the original name doesn't match the ghidra-ized name
*/ */
public boolean isNameModified() { public boolean isNameModified() {
return originalName == null || !originalName.equals(namespacePath.getName()); 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, public DWARFNameInfo createChild(String childOriginalName, String childName,
SymbolType childType) { SymbolType childType) {
return new DWARFNameInfo(this, childOriginalName, childName, childType); return new DWARFNameInfo(this, childOriginalName, childName, childType);

View file

@ -170,21 +170,13 @@ public class DWARFParser {
if (origRoot.equals(cp)) { if (origRoot.equals(cp)) {
return newRoot; return newRoot;
} }
List<String> origRootParts = origRoot.asList();
String origRootPath = origRoot.getPath(); List<String> cpParts = cp.asList();
if (!CategoryPath.ROOT.equals(origRoot)) { if (cpParts.size() < origRootParts.size() ||
origRootPath += "/"; !origRootParts.equals(cpParts.subList(0, origRootParts.size()))) {
return null;
} }
String newRootPath = newRoot.getPath(); return new CategoryPath(newRoot, cpParts.subList(origRootParts.size(), cpParts.size()));
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;
} }
/** /**

View file

@ -15,10 +15,9 @@
*/ */
package ghidra.app.util.bin.format.dwarf4.next; package ghidra.app.util.bin.format.dwarf4.next;
import java.util.*;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.*;
import org.apache.commons.collections4.ListValuedMap; import org.apache.commons.collections4.ListValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
@ -396,14 +395,6 @@ public class DWARFProgram implements Closeable {
String origName = isAnon ? null : name; String origName = isAnon ? null : name;
String workingName = ensureSafeNameLength(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 = DWARFNameInfo result =
parentDNI.createChild(origName, workingName, DWARFUtil.getSymbolTypeFromDIE(diea)); parentDNI.createChild(origName, workingName, DWARFUtil.getSymbolTypeFromDIE(diea));

View file

@ -15,10 +15,10 @@
*/ */
package ghidra.app.util.bin.format.dwarf4.next; package ghidra.app.util.bin.format.dwarf4.next;
import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import ghidra.app.util.NamespaceUtils; import ghidra.app.util.NamespaceUtils;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -55,13 +55,8 @@ public class NamespacePath implements Comparable<NamespacePath> {
* @return new {@link NamespacePath} * @return new {@link NamespacePath}
*/ */
public static NamespacePath create(NamespacePath parent, String name, SymbolType type) { public static NamespacePath create(NamespacePath parent, String name, SymbolType type) {
return new NamespacePath(parent == null ? ROOT : parent, preMangleName(name), type); return new NamespacePath(Objects.requireNonNullElse(parent, ROOT),
} SymbolUtilities.replaceInvalidChars(name, true), type);
private static final String FWDSLASH_MANGLE = "-fwdslash-";
private static String preMangleName(String name) {
return name == null ? null : name.replaceAll(" ", "").replaceAll("/", FWDSLASH_MANGLE);
} }
private final NamespacePath parent; private final NamespacePath parent;
@ -171,17 +166,6 @@ public class NamespacePath implements Comparable<NamespacePath> {
} }
} }
/**
* 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. * Converts this namespace path into a {@link Namespace} style string.
* @return string path "ROOT::namespace1::namespace2" * @return string path "ROOT::namespace1::namespace2"
@ -212,6 +196,21 @@ public class NamespacePath implements Comparable<NamespacePath> {
return sb.toString(); 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<String> getParts() {
List<String> partList = new ArrayList<>();
doInOrderTraversal(nsp -> {
if (!nsp.isRoot()) {
partList.add(nsp.name);
}
});
return partList;
}
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

View file

@ -894,6 +894,71 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
return results; 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<int, float>", 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<int,_float>");
assertEquals(2, structdt.getNumDefinedComponents());
}
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@Test @Test

View file

@ -18,15 +18,17 @@ package ghidra.app.util.bin.format.dwarf4.next;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf4.*;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage; import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes; import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Parameter; import ghidra.program.model.symbol.Namespace;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
public class DWARFFunctionImporterTest extends DWARFTestBase { public class DWARFFunctionImporterTest extends DWARFTestBase {
@ -113,4 +115,46 @@ public class DWARFFunctionImporterTest extends DWARFTestBase {
assertNotNull(returnType); assertNotNull(returnType);
assertEquals("int", returnType.getName()); 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<ns1::ns2::argclass>)
// 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<Namespace> 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);
}
} }

View file

@ -15,15 +15,16 @@
*/ */
package ghidra.app.util.bin.format.dwarf4.next; package ghidra.app.util.bin.format.dwarf4.next;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import java.io.IOException; import java.io.IOException;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf4.*;
import ghidra.program.model.data.*; 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.program.model.symbol.SymbolType;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -35,8 +36,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
@Test @Test
public void testSimple() { public void testSimple() {
DWARFNameInfo dni = root.createChild("simplename", "simplename", SymbolType.CLASS); DWARFNameInfo dni = root.createChild("simplename", "simplename", SymbolType.CLASS);
Assert.assertEquals(new CategoryPath("/TEST_DNI/simplename"), dni.asCategoryPath()); assertEquals(new CategoryPath("/TEST_DNI/simplename"), dni.asCategoryPath());
Assert.assertEquals( assertEquals(
NamespacePath.create(NamespacePath.ROOT, "simplename", SymbolType.CLASS), NamespacePath.create(NamespacePath.ROOT, "simplename", SymbolType.CLASS),
dni.getNamespacePath()); dni.getNamespacePath());
} }
@ -44,12 +45,69 @@ public class DWARFNameInfoTest extends DWARFTestBase {
@Test @Test
public void testSlash() { public void testSlash() {
DWARFNameInfo dni = root.createChild("blah/", "blah/", SymbolType.CLASS); DWARFNameInfo dni = root.createChild("blah/", "blah/", SymbolType.CLASS);
Assert.assertEquals(new CategoryPath("/TEST_DNI/blah-fwdslash-"), dni.asCategoryPath()); assertEquals(new CategoryPath(CategoryPath.ROOT, "TEST_DNI", "blah/"),
Assert.assertEquals( dni.asCategoryPath());
NamespacePath.create(NamespacePath.ROOT, "blah-fwdslash-", SymbolType.CLASS),
Namespace testNamespace = dni.asNamespace(program);
assertEquals("blah/", testNamespace.getName(true));
assertEquals(
NamespacePath.create(NamespacePath.ROOT, "blah/", SymbolType.CLASS),
dni.getNamespacePath()); 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 @Test
public void testNestedStructNames() throws CancelledException, IOException, DWARFException { public void testNestedStructNames() throws CancelledException, IOException, DWARFException {
DebugInfoEntry structDIE = newStruct("struct", 100).create(cu); DebugInfoEntry structDIE = newStruct("struct", 100).create(cu);
@ -60,8 +118,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null);
DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null);
Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); assertEquals(rootCP.getPath() + "/struct", structDT.getPathName());
Assert.assertEquals(rootCP.getPath() + "/struct/substruct", substructDT.getPathName()); assertEquals(rootCP.getPath() + "/struct/substruct", substructDT.getPathName());
} }
@Test @Test
@ -75,8 +133,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null);
DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null);
Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); assertEquals(rootCP.getPath() + "/struct", structDT.getPathName());
Assert.assertEquals(rootCP.getPath() + "/struct/anon_struct_0", substructDT.getPathName()); assertEquals(rootCP.getPath() + "/struct/anon_struct_0", substructDT.getPathName());
} }
@Test @Test
@ -91,8 +149,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null);
DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null);
Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); assertEquals(rootCP.getPath() + "/struct", structDT.getPathName());
Assert.assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1", assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1",
substructDT.getPathName()); substructDT.getPathName());
} }
@ -109,8 +167,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null); DataType structDT = dwarfDTM.getDataType(structDIE.getOffset(), null);
DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null); DataType substructDT = dwarfDTM.getDataType(substructDIE.getOffset(), null);
Assert.assertEquals(rootCP.getPath() + "/struct", structDT.getPathName()); assertEquals(rootCP.getPath() + "/struct", structDT.getPathName());
Assert.assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1_f2", assertEquals(rootCP.getPath() + "/struct/anon_struct_for_f1_f2",
substructDT.getPathName()); substructDT.getPathName());
} }
@ -125,12 +183,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
importAllDataTypes(); importAllDataTypes();
Structure structDT = (Structure) dwarfDTM.getDataType(structDIE.getOffset(), null); 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 @Test
@ -143,12 +197,8 @@ public class DWARFNameInfoTest extends DWARFTestBase {
importAllDataTypes(); importAllDataTypes();
Structure structDT = (Structure) dwarfDTM.getDataType(structDIE.getOffset(), null); 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 @Test

View file

@ -30,9 +30,6 @@ public class NamespacePathTest {
Assert.assertEquals("ROOT::sub1", nsp.asNamespaceString()); Assert.assertEquals("ROOT::sub1", nsp.asNamespaceString());
Assert.assertEquals("ROOT::sub1::sub1_1", nsp1_1.asNamespaceString()); Assert.assertEquals("ROOT::sub1::sub1_1", nsp1_1.asNamespaceString());
Assert.assertEquals("/sub1", nsp.asCategoryPathString());
Assert.assertEquals("/sub1/sub1_1", nsp1_1.asCategoryPathString());
} }
@Test @Test
@ -41,8 +38,12 @@ public class NamespacePathTest {
NamespacePath nsSpaceA = NamespacePath.create(null, "ns A", SymbolType.NAMESPACE); NamespacePath nsSpaceA = NamespacePath.create(null, "ns A", SymbolType.NAMESPACE);
NamespacePath nsColonA = 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::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());
} }
} }

View file

@ -537,4 +537,22 @@ public class NamespaceUtils {
SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable(); SymbolTable symbolTable = namespaceSymbol.getProgram().getSymbolTable();
return symbolTable.convertNamespaceToClass(namespace); 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.
* <p>
* Namespace "ns1::ns2::ns3" returns [ "ns1", "ns1::ns2", "ns1::ns2::ns3" ]
*
* @param namespace namespace to process
* @return list of namespaces
*/
public static List<Namespace> getNameSpaceParts(Namespace namespace) {
List<Namespace> result = new ArrayList<>();
while (!namespace.isGlobal()) {
result.add(0, namespace);
namespace = namespace.getParentNamespace();
}
return result;
}
} }

View file

@ -18,6 +18,7 @@ package ghidra.program.database.data;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import ghidra.app.util.NamespaceUtils;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
import ghidra.program.model.address.GlobalNamespace; import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -341,23 +342,15 @@ public class DataTypeUtilities {
*/ */
public static CategoryPath getDataTypeCategoryPath(CategoryPath baseCategory, public static CategoryPath getDataTypeCategoryPath(CategoryPath baseCategory,
Namespace namespace) { Namespace namespace) {
Namespace ns = namespace; List<String> categoryPathParts = new ArrayList<>();
String path = ""; for (Namespace ns : NamespaceUtils.getNameSpaceParts(namespace)) {
while (!ns.isGlobal() && !(ns instanceof Library)) { if (!(ns instanceof Library)) {
if (path.length() != 0) { categoryPathParts.add(ns.getName());
path = "/" + path;
} }
path = ns.getName() + path;
ns = ns.getParentNamespace();
} }
if (path.length() == 0) { return categoryPathParts.isEmpty()
return baseCategory; ? baseCategory
} : new CategoryPath(baseCategory, categoryPathParts);
String categoryPath = CategoryPath.DELIMITER_CHAR + path;
if (!baseCategory.equals(CategoryPath.ROOT)) {
categoryPath = baseCategory.getPath() + categoryPath;
}
return new CategoryPath(categoryPath);
} }
/** /**

View file

@ -364,7 +364,7 @@ public class SymbolUtilities {
return null; return null;
} }
int len = str.length(); int len = str.length();
StringBuffer buf = new StringBuffer(len); StringBuilder buf = new StringBuilder(len);
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
char c = str.charAt(i); char c = str.charAt(i);
if (isInvalidChar(c)) { if (isInvalidChar(c)) {