mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Changes Program Graph actions to not graph entire program when there is no selection
This commit is contained in:
parent
5b8be18b40
commit
c02ab0160c
4 changed files with 135 additions and 65 deletions
|
@ -45,11 +45,41 @@
|
|||
systems that have convoluted control flow structure, it may be beneficial to graph the entire
|
||||
program.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Graph Scope</H2>
|
||||
<P>When creating any of the graphs described below, the scope of the graph is determined by the
|
||||
current location and selection present in the listing. </P>
|
||||
<BLOCKQUOTE>
|
||||
<H3>If there is a selection:</H3>
|
||||
<BLOCKQUOTE><P> The graph will
|
||||
include all the code that is selected and exclude all the code that is not selected.</P>
|
||||
</BLOCKQUOTE>
|
||||
<H3>If there is no selection:</H3>
|
||||
<BLOCKQUOTE>
|
||||
<P> The scope is determined by the current cursor location and the type of graph:</P>
|
||||
</BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<H4>Current Location is in a Function</H4>
|
||||
<UL>
|
||||
<LI>Block Flow Graphs: The scope is the body of the containing functions</LI>
|
||||
<LI>Call Graphs: The scope is the containing function and all the functions that
|
||||
either call it or it calls</LI>
|
||||
</UL>
|
||||
<H4>Current Location is not in a Function</H4>
|
||||
<UL>
|
||||
<LI>the scope will be the entire program</LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/tip.png"> To graph the entire program, press "<ctrl> a" to
|
||||
select all before creating the graph.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Synchronization</H2>
|
||||
|
||||
<P>Selection and Location events are synchronized between each
|
||||
graph and the other windows in the tool.
|
||||
|
||||
<H2>Selection</H2>
|
||||
<BLOCKQUOTE>
|
||||
<H3>Selection</H3>
|
||||
|
||||
<P>The current selection within the graph display is represented by a red box around selected
|
||||
nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the
|
||||
|
@ -74,7 +104,7 @@
|
|||
from the basic blocks found within the selected subroutine.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Location</H2>
|
||||
<H3>Location</H3>
|
||||
|
||||
<P>The node containing the current address location is marked with a large red arrow as shown
|
||||
below on the graph node labeled "00408133".</P>
|
||||
|
@ -94,7 +124,7 @@
|
|||
<P align="left">Clicking on a node in the graph display causes the
|
||||
current address location within Ghidra to change to the minimum address represented by the
|
||||
graph node.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
<H2>Graph Representation</H2>
|
||||
|
||||
<P>By Default, the graphs use the following icons and colors to represent the nodes and edges.</P>
|
||||
|
|
|
@ -21,8 +21,7 @@ import java.util.*;
|
|||
import docking.widgets.EventTrigger;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.block.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
@ -110,8 +109,9 @@ public class BlockGraphTask extends Task {
|
|||
private boolean reuseGraph;
|
||||
private boolean appendGraph;
|
||||
private PluginTool tool;
|
||||
private String actionName;
|
||||
private Program program;
|
||||
private AddressSetView graphScope;
|
||||
private String graphTitle;
|
||||
|
||||
public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode,
|
||||
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
|
||||
|
@ -119,8 +119,6 @@ public class BlockGraphTask extends Task {
|
|||
GraphDisplayProvider graphProvider) {
|
||||
|
||||
super("Graph Program", true, false, true);
|
||||
this.actionName = actionName;
|
||||
|
||||
this.graphEntryPointNexus = graphEntryPointNexus;
|
||||
this.showCode = showCode;
|
||||
this.reuseGraph = reuseGraph;
|
||||
|
@ -132,6 +130,7 @@ public class BlockGraphTask extends Task {
|
|||
this.selection = selection;
|
||||
this.location = location;
|
||||
this.program = blockModel.getProgram();
|
||||
this.graphTitle = actionName + ": ";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,6 +138,7 @@ public class BlockGraphTask extends Task {
|
|||
*/
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
this.graphScope = getGraphScopeAndGenerateGraphTitle();
|
||||
AttributedGraph graph = createGraph();
|
||||
monitor.setMessage("Generating Graph...");
|
||||
try {
|
||||
|
@ -153,7 +153,7 @@ public class BlockGraphTask extends Task {
|
|||
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
||||
codeLimitPerBlock + 1);
|
||||
}
|
||||
display.setGraph(graph, getDescription(), appendGraph, monitor);
|
||||
display.setGraph(graph, graphTitle, appendGraph, monitor);
|
||||
|
||||
if (location != null) {
|
||||
// initialize the graph location, but don't have the graph send an event
|
||||
|
@ -175,17 +175,6 @@ public class BlockGraphTask extends Task {
|
|||
}
|
||||
}
|
||||
|
||||
private String getDescription() {
|
||||
String description = actionName;
|
||||
if (selection != null && !selection.isEmpty()) {
|
||||
description += ": " + selection.getMinAddress();
|
||||
}
|
||||
else {
|
||||
description += " (Entire Program)";
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of code lines which will be used per block when
|
||||
* showCode is enabled.
|
||||
|
@ -221,10 +210,62 @@ public class BlockGraphTask extends Task {
|
|||
}
|
||||
|
||||
private CodeBlockIterator getBlockIterator() throws CancelledException {
|
||||
if (selection == null || selection.isEmpty()) {
|
||||
return blockModel.getCodeBlocks(taskMonitor);
|
||||
return blockModel.getCodeBlocksContaining(graphScope, taskMonitor);
|
||||
}
|
||||
|
||||
private AddressSetView getGraphScopeAndGenerateGraphTitle() {
|
||||
if (selection != null && !selection.isEmpty()) {
|
||||
graphTitle += selection.getMinAddress().toString();
|
||||
return selection;
|
||||
}
|
||||
return blockModel.getCodeBlocksContaining(selection, taskMonitor);
|
||||
Function function = getContainingFunction(location);
|
||||
if (function != null) {
|
||||
graphTitle += function.getName();
|
||||
if (isCallGraph()) {
|
||||
return getScopeForCallGraph(function);
|
||||
}
|
||||
return function.getBody();
|
||||
}
|
||||
graphTitle += "(Entire Program)";
|
||||
return blockModel.getProgram().getMemory();
|
||||
}
|
||||
|
||||
private boolean isCallGraph() {
|
||||
return blockModel instanceof SubroutineBlockModel;
|
||||
}
|
||||
|
||||
private AddressSetView getScopeForCallGraph(Function function) {
|
||||
AddressSet set = new AddressSet();
|
||||
set.add(function.getBody());
|
||||
try {
|
||||
CodeBlock block = blockModel.getCodeBlockAt(function.getEntryPoint(), taskMonitor);
|
||||
CodeBlockReferenceIterator it = blockModel.getDestinations(block, taskMonitor);
|
||||
while (it.hasNext()) {
|
||||
CodeBlockReference next = it.next();
|
||||
set.add(next.getDestinationBlock());
|
||||
}
|
||||
it = blockModel.getSources(block, taskMonitor);
|
||||
while (it.hasNext()) {
|
||||
CodeBlockReference next = it.next();
|
||||
set.add(next.getSourceBlock());
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// just return, the task is being cancelled.
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private Function getContainingFunction(ProgramLocation cursorLocation) {
|
||||
if (cursorLocation == null) {
|
||||
return null;
|
||||
}
|
||||
Address address = cursorLocation.getAddress();
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
return blockModel.getProgram().getFunctionManager().getFunctionContaining(address);
|
||||
}
|
||||
|
||||
private Address graphBlock(AttributedGraph graph, CodeBlock curBB,
|
||||
|
@ -285,7 +326,7 @@ public class BlockGraphTask extends Task {
|
|||
|
||||
// don't include destination if it does not overlap selection
|
||||
// always include if selection is empty
|
||||
if (selection != null && !selection.isEmpty() && !selection.intersects(db)) {
|
||||
if (graphScope != null && !graphScope.isEmpty() && !graphScope.intersects(db)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,49 +177,49 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
|||
private void createActions() {
|
||||
|
||||
new ActionBuilder("Graph Block Flow", getName())
|
||||
.menuPath(MENU_GRAPH, "&Block Flow")
|
||||
.menuGroup("Graph", "A")
|
||||
.onAction(c -> graphBlockFlow())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath(MENU_GRAPH, "&Block Flow")
|
||||
.menuGroup("Graph", "A")
|
||||
.onAction(c -> graphBlockFlow())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Graph Code Flow", getName())
|
||||
.menuPath(MENU_GRAPH, "C&ode Flow")
|
||||
.menuGroup("Graph", "B")
|
||||
.onAction(c -> graphCodeFlow())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath(MENU_GRAPH, "C&ode Flow")
|
||||
.menuGroup("Graph", "B")
|
||||
.onAction(c -> graphCodeFlow())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Graph Calls Using Default Model", getName())
|
||||
.menuPath(MENU_GRAPH, "&Calls")
|
||||
.menuGroup("Graph", "C")
|
||||
.onAction(c -> graphSubroutines())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath(MENU_GRAPH, "&Calls")
|
||||
.menuGroup("Graph", "C")
|
||||
.onAction(c -> graphSubroutines())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
reuseGraphAction = new ToggleActionBuilder("Reuse Graph", getName())
|
||||
.menuPath(MENU_GRAPH, "Reuse Graph")
|
||||
.menuGroup("Graph Options")
|
||||
.selected(reuseGraph)
|
||||
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath(MENU_GRAPH, "Reuse Graph")
|
||||
.menuGroup("Graph Options")
|
||||
.selected(reuseGraph)
|
||||
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
appendGraphAction = new ToggleActionBuilder("Append Graph", getName())
|
||||
.menuPath(MENU_GRAPH, "Append Graph")
|
||||
.menuGroup("Graph Options")
|
||||
.selected(false)
|
||||
.onAction(c -> updateAppendAndReuseGraph())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath(MENU_GRAPH, "Append Graph")
|
||||
.menuGroup("Graph Options")
|
||||
.selected(false)
|
||||
.onAction(c -> updateAppendAndReuseGraph())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
forceLocationVisibleAction = new ToggleActionBuilder("Show Location in Graph", getName())
|
||||
.menuPath(MENU_GRAPH, "Show Location")
|
||||
.description("Tell the graph to pan/scale as need to keep location changes visible")
|
||||
.menuGroup("Graph Options")
|
||||
.onAction(c -> toggleForceLocationVisible())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath(MENU_GRAPH, "Show Location")
|
||||
.description("Tell the graph to pan/scale as need to keep location changes visible")
|
||||
.menuGroup("Graph Options")
|
||||
.onAction(c -> toggleForceLocationVisible())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
updateSubroutineActions();
|
||||
}
|
||||
|
@ -268,12 +268,12 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
|||
|
||||
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
||||
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
||||
.menuPath("Graph", "Calls Using Model", blockModelName)
|
||||
.menuGroup("Graph")
|
||||
.helpLocation(helpLoc)
|
||||
.onAction(c -> graphSubroutinesUsing(blockModelName))
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
.menuPath("Graph", "Calls Using Model", blockModelName)
|
||||
.menuGroup("Graph")
|
||||
.helpLocation(helpLoc)
|
||||
.onAction(c -> graphSubroutinesUsing(blockModelName))
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
}
|
||||
|
||||
private void graphBlockFlow() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue