mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09: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;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
|||||||
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
||||||
import ghidra.app.services.GoToService;
|
import ghidra.app.services.GoToService;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.app.util.NamespaceUtils;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
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 {
|
private void function(int from, int to) throws Exception {
|
||||||
ensureFunction(from);
|
ensureFunction(from);
|
||||||
ensureFunction(to);
|
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();
|
ProgramDB p = builder.getProgram();
|
||||||
FunctionManager fm = p.getFunctionManager();
|
FunctionManager fm = p.getFunctionManager();
|
||||||
Function f = fm.getFunctionAt(addr(from));
|
Function f = fm.getFunctionAt(addr(from));
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
return;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
String a = Long.toHexString(from);
|
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) {
|
private boolean createReference(long from, long to) {
|
||||||
@@ -580,12 +608,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
|
|
||||||
setProviderFunction("0x5000");
|
setProviderFunction("0x5000");
|
||||||
|
|
||||||
final ToggleDockingAction filterDuplicatesAction =
|
ToggleDockingAction filterDuplicatesAction =
|
||||||
(ToggleDockingAction) getAction("Filter Duplicates");
|
(ToggleDockingAction) getAction("Filter Duplicates");
|
||||||
|
|
||||||
if (!filterDuplicatesAction.isSelected()) {
|
setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
|
||||||
performAction(filterDuplicatesAction, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForTree(outgoingTree);
|
waitForTree(outgoingTree);
|
||||||
GTreeNode rootNode = getRootNode(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
|
// copy the names of the children into a map so that we can verify that there are
|
||||||
// no duplicates
|
// no duplicates
|
||||||
boolean shouldHaveDuplicates = false;
|
boolean shouldHaveDuplicates = false;
|
||||||
Map<String, Integer> nameCountMap = createNameCountMap(rootNode);
|
Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
|
||||||
System.out.println("nameCountMap: before: = " + nameCountMap);
|
|
||||||
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||||
|
|
||||||
performAction(filterDuplicatesAction, true);// deselect
|
performAction(filterDuplicatesAction, true);// deselect
|
||||||
@@ -608,7 +633,47 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
rootNode = getRootNode(outgoingTree);
|
rootNode = getRootNode(outgoingTree);
|
||||||
nameCountMap = createNameCountMap(rootNode);
|
nameCountMap = createNameCountMap(rootNode);
|
||||||
shouldHaveDuplicates = true;
|
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);
|
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1071,27 +1136,26 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
return codeBrowserPlugin.getCurrentAddress();
|
return codeBrowserPlugin.getCurrentAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Integer> createNameCountMap(GTreeNode node) {
|
private Map<GTreeNode, Integer> createNameCountMap(GTreeNode node) {
|
||||||
Map<String, Integer> map = new HashMap<>();
|
Map<GTreeNode, Integer> map = new HashMap<>();
|
||||||
List<GTreeNode> children = node.getChildren();
|
List<GTreeNode> children = node.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
String name = child.getName();
|
Integer integer = map.get(child);
|
||||||
Integer integer = map.get(name);
|
|
||||||
if (integer == null) {
|
if (integer == null) {
|
||||||
integer = 0;
|
integer = 0;
|
||||||
}
|
}
|
||||||
int asInt = integer;
|
int asInt = integer;
|
||||||
asInt++;
|
asInt++;
|
||||||
map.put(name, asInt);
|
map.put(child, asInt);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDuplicateChildStatus(Map<String, Integer> childCountMap,
|
private void assertDuplicateChildStatus(Map<GTreeNode, Integer> childCountMap,
|
||||||
boolean shouldHaveDuplicates) {
|
boolean shouldHaveDuplicates) {
|
||||||
boolean foundDuplicates = false;
|
boolean foundDuplicates = false;
|
||||||
Set<Entry<String, Integer>> entrySet = childCountMap.entrySet();
|
Set<Entry<GTreeNode, Integer>> entrySet = childCountMap.entrySet();
|
||||||
for (Entry<String, Integer> entry : entrySet) {
|
for (Entry<GTreeNode, Integer> entry : entrySet) {
|
||||||
int value = entry.getValue();
|
int value = entry.getValue();
|
||||||
if (value != 1) {
|
if (value != 1) {
|
||||||
foundDuplicates = true;
|
foundDuplicates = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user