mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-22 04:52:18 +08:00
Merge branch 'GP-213-dragonmacher-call-tree-duplicates' into Ghidra_9.2
This commit is contained in:
@@ -141,4 +141,28 @@ public abstract class CallNode extends GTreeSlowLoadingNode {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// overridden since we may have multiple children with the same function name, but in
|
||||
// different namespaces
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CallNode other = (CallNode) obj;
|
||||
return getSourceAddress().equals(other.getSourceAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getSourceAddress().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+82
-18
@@ -42,6 +42,7 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
@@ -158,6 +159,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
private Function function(int addr) throws Exception {
|
||||
return ensureFunction(addr);
|
||||
}
|
||||
|
||||
private void function(int from, int to) throws Exception {
|
||||
ensureFunction(from);
|
||||
ensureFunction(to);
|
||||
@@ -168,16 +173,39 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureFunction(long from) throws Exception {
|
||||
private Function function(String namespace, String name, int addr) throws Exception {
|
||||
Function f = ensureFunction(addr);
|
||||
tx(program, () -> {
|
||||
|
||||
f.setName(name, SourceType.ANALYSIS);
|
||||
Namespace ns = NamespaceUtils.createNamespaceHierarchy(namespace, null, program,
|
||||
SourceType.ANALYSIS);
|
||||
f.setParentNamespace(ns);
|
||||
});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
private void function(Function from, Function to) {
|
||||
|
||||
// a bit of space so the function call is not at the entry point
|
||||
int offset = (int) from.getEntryPoint().getOffset() + 5;
|
||||
int toOffset = (int) to.getEntryPoint().getOffset();
|
||||
while (!createReference(offset, toOffset)) {
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
private Function ensureFunction(long from) throws Exception {
|
||||
ProgramDB p = builder.getProgram();
|
||||
FunctionManager fm = p.getFunctionManager();
|
||||
Function f = fm.getFunctionAt(addr(from));
|
||||
if (f != null) {
|
||||
return;
|
||||
return f;
|
||||
}
|
||||
|
||||
String a = Long.toHexString(from);
|
||||
builder.createEmptyFunction("Function_" + a, "0x" + a, 50, DataType.DEFAULT);
|
||||
return builder.createEmptyFunction("Function_" + a, "0x" + a, 50, DataType.DEFAULT);
|
||||
}
|
||||
|
||||
private boolean createReference(long from, long to) {
|
||||
@@ -580,12 +608,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
setProviderFunction("0x5000");
|
||||
|
||||
final ToggleDockingAction filterDuplicatesAction =
|
||||
ToggleDockingAction filterDuplicatesAction =
|
||||
(ToggleDockingAction) getAction("Filter Duplicates");
|
||||
|
||||
if (!filterDuplicatesAction.isSelected()) {
|
||||
performAction(filterDuplicatesAction, true);
|
||||
}
|
||||
setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
|
||||
|
||||
waitForTree(outgoingTree);
|
||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||
@@ -597,8 +623,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
// copy the names of the children into a map so that we can verify that there are
|
||||
// no duplicates
|
||||
boolean shouldHaveDuplicates = false;
|
||||
Map<String, Integer> nameCountMap = createNameCountMap(rootNode);
|
||||
System.out.println("nameCountMap: before: = " + nameCountMap);
|
||||
Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
|
||||
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||
|
||||
performAction(filterDuplicatesAction, true);// deselect
|
||||
@@ -608,7 +633,47 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
rootNode = getRootNode(outgoingTree);
|
||||
nameCountMap = createNameCountMap(rootNode);
|
||||
shouldHaveDuplicates = true;
|
||||
System.out.println("nameCountMap: after: = " + nameCountMap);
|
||||
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterIncomingDuplicates_SameFunctionNameInDifferentNamespaces()
|
||||
throws Exception {
|
||||
|
||||
/*
|
||||
Create a function call tree that looks like:
|
||||
|
||||
|
||||
NS1::Foo
|
||||
root
|
||||
NS2::Foo
|
||||
root
|
||||
|
||||
*/
|
||||
|
||||
Function f1 = function("NS1", "Foo", 0x11000);
|
||||
Function f2 = function("NS2", "Foo", 0x12000);
|
||||
Function root = function(0x0000);
|
||||
function(f1, root);
|
||||
function(f2, root);
|
||||
|
||||
setProviderFunction("0x0000");
|
||||
|
||||
ToggleDockingAction filterDuplicatesAction =
|
||||
(ToggleDockingAction) getAction("Filter Duplicates");
|
||||
setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
|
||||
waitForTree(incomingTree);
|
||||
|
||||
GTreeNode rootNode = getRootNode(incomingTree);
|
||||
List<GTreeNode> children = rootNode.getChildren();
|
||||
assertTrue(
|
||||
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||
children.size() > 0);
|
||||
|
||||
// copy the names of the children into a map so that we can verify that there are
|
||||
// no duplicates
|
||||
boolean shouldHaveDuplicates = false;
|
||||
Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
|
||||
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||
}
|
||||
|
||||
@@ -1071,27 +1136,26 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
return codeBrowserPlugin.getCurrentAddress();
|
||||
}
|
||||
|
||||
private Map<String, Integer> createNameCountMap(GTreeNode node) {
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
private Map<GTreeNode, Integer> createNameCountMap(GTreeNode node) {
|
||||
Map<GTreeNode, Integer> map = new HashMap<>();
|
||||
List<GTreeNode> children = node.getChildren();
|
||||
for (GTreeNode child : children) {
|
||||
String name = child.getName();
|
||||
Integer integer = map.get(name);
|
||||
Integer integer = map.get(child);
|
||||
if (integer == null) {
|
||||
integer = 0;
|
||||
}
|
||||
int asInt = integer;
|
||||
asInt++;
|
||||
map.put(name, asInt);
|
||||
map.put(child, asInt);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void assertDuplicateChildStatus(Map<String, Integer> childCountMap,
|
||||
private void assertDuplicateChildStatus(Map<GTreeNode, Integer> childCountMap,
|
||||
boolean shouldHaveDuplicates) {
|
||||
boolean foundDuplicates = false;
|
||||
Set<Entry<String, Integer>> entrySet = childCountMap.entrySet();
|
||||
for (Entry<String, Integer> entry : entrySet) {
|
||||
Set<Entry<GTreeNode, Integer>> entrySet = childCountMap.entrySet();
|
||||
for (Entry<GTreeNode, Integer> entry : entrySet) {
|
||||
int value = entry.getValue();
|
||||
if (value != 1) {
|
||||
foundDuplicates = true;
|
||||
|
||||
Reference in New Issue
Block a user