mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-3165 - Tables - Fixed Ctrl-A conflict between tables and the Listing
This commit is contained in:
parent
ab5e715607
commit
a87b65b758
5 changed files with 443 additions and 336 deletions
|
@ -24,7 +24,6 @@ import javax.swing.*;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.label.GIconLabel;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import resources.ResourceManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog to show multiple actions that are mapped to the same keystroke;
|
* Dialog to show multiple actions that are mapped to the same keystroke;
|
||||||
|
@ -39,10 +38,8 @@ public class ActionDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param parent parent to this dialog
|
|
||||||
* @param keystrokeName keystroke name
|
* @param keystrokeName keystroke name
|
||||||
* @param list list of PluginActions
|
* @param list list of actions
|
||||||
* @param event event to pass the selected action
|
|
||||||
*/
|
*/
|
||||||
public ActionDialog(String keystrokeName, List<ExecutableKeyActionAdapter> list) {
|
public ActionDialog(String keystrokeName, List<ExecutableKeyActionAdapter> list) {
|
||||||
super("Select Action", true);
|
super("Select Action", true);
|
||||||
|
@ -68,10 +65,8 @@ public class ActionDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the list of actions that are enabled.
|
* Set the list of actions that are enabled
|
||||||
* @param list list of actions
|
* @param list list of actions selected
|
||||||
* @param event event to pass to the action that is
|
|
||||||
* selected.
|
|
||||||
*/
|
*/
|
||||||
public void setActionList(List<ExecutableKeyActionAdapter> list) {
|
public void setActionList(List<ExecutableKeyActionAdapter> list) {
|
||||||
okButton.setEnabled(false);
|
okButton.setEnabled(false);
|
||||||
|
@ -85,8 +80,6 @@ public class ActionDialog extends DialogComponentProvider {
|
||||||
actionList.setSelectedIndex(0);
|
actionList.setSelectedIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
this.addWorkPanel(buildMainPanel());
|
this.addWorkPanel(buildMainPanel());
|
||||||
addOKButton();
|
addOKButton();
|
||||||
|
@ -107,7 +100,9 @@ public class ActionDialog extends DialogComponentProvider {
|
||||||
innerPanel.setBorder(BorderFactory.createTitledBorder("Actions"));
|
innerPanel.setBorder(BorderFactory.createTitledBorder("Actions"));
|
||||||
|
|
||||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
panel.add(new GIconLabel(ResourceManager.loadImage("images/warning.png")));
|
|
||||||
|
Icon icon = UIManager.getIcon("OptionPane.warningIcon");
|
||||||
|
panel.add(new GIconLabel(icon));
|
||||||
panel.add(labelPanel);
|
panel.add(labelPanel);
|
||||||
|
|
||||||
listModel = new DefaultListModel<>();
|
listModel = new DefaultListModel<>();
|
||||||
|
@ -139,7 +134,7 @@ public class ActionDialog extends DialogComponentProvider {
|
||||||
return mainPanel;
|
return mainPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addListeners() {
|
private void addListeners() {
|
||||||
actionList.addMouseListener(new MouseAdapter() {
|
actionList.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.action;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.ComponentProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to signal that the implementing action works with an individual Java
|
||||||
|
* {@link Component}. Standard Docking Actions are either global tool-based actions or local
|
||||||
|
* {@link ComponentProvider} actions. This interface allows us to have the concept of an
|
||||||
|
* action that is effectively local to a specific Java component.
|
||||||
|
*/
|
||||||
|
public interface ComponentBasedDockingAction extends DockingActionIf {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given context contains this action's component
|
||||||
|
* @param context the context
|
||||||
|
* @return true if the given context contains this action's component
|
||||||
|
*/
|
||||||
|
public boolean isValidComponentContext(ActionContext context);
|
||||||
|
}
|
|
@ -169,9 +169,11 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||||
List<ExecutableKeyActionAdapter> list = new ArrayList<>();
|
List<ExecutableKeyActionAdapter> list = new ArrayList<>();
|
||||||
boolean hasLocalActionsForKeyBinding = false;
|
boolean hasLocalActionsForKeyBinding = false;
|
||||||
|
|
||||||
// search for local actions first...
|
//
|
||||||
|
// 1) Prefer local actions for the active provider
|
||||||
|
//
|
||||||
for (ActionData actionData : actions) {
|
for (ActionData actionData : actions) {
|
||||||
if (actionData.isMyProvider(localContext.getComponentProvider())) {
|
if (actionData.isMyProvider(localContext)) {
|
||||||
hasLocalActionsForKeyBinding = true;
|
hasLocalActionsForKeyBinding = true;
|
||||||
if (actionData.action.isEnabledForContext(localContext)) {
|
if (actionData.action.isEnabledForContext(localContext)) {
|
||||||
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext));
|
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext));
|
||||||
|
@ -185,7 +187,33 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...no locals, see if we have any global actions
|
//
|
||||||
|
// 2) Check for actions local to the source component
|
||||||
|
//
|
||||||
|
for (ActionData actionData : actions) {
|
||||||
|
if (!(actionData.action instanceof ComponentBasedDockingAction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentBasedDockingAction componentAction =
|
||||||
|
(ComponentBasedDockingAction) actionData.action;
|
||||||
|
if (componentAction.isValidComponentContext(localContext)) {
|
||||||
|
hasLocalActionsForKeyBinding = true;
|
||||||
|
if (actionData.action.isEnabledForContext(localContext)) {
|
||||||
|
list.add(new ExecutableKeyActionAdapter(actionData.action, localContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLocalActionsForKeyBinding) {
|
||||||
|
// We have locals, ignore the globals. This prevents global actions from processing
|
||||||
|
// the given keybinding when a local action exits, regardless of enablement.
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 3) Check for global actions
|
||||||
|
//
|
||||||
for (ActionData actionData : actions) {
|
for (ActionData actionData : actions) {
|
||||||
if (actionData.isGlobalAction()) {
|
if (actionData.isGlobalAction()) {
|
||||||
// When looking for context matches, we prefer local context, even though this
|
// When looking for context matches, we prefer local context, even though this
|
||||||
|
@ -271,7 +299,8 @@ public class MultipleKeyAction extends DockingKeyBindingAction {
|
||||||
return provider == null;
|
return provider == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isMyProvider(ComponentProvider otherProvider) {
|
boolean isMyProvider(ActionContext localContext) {
|
||||||
|
ComponentProvider otherProvider = localContext.getComponentProvider();
|
||||||
return provider == otherProvider;
|
return provider == otherProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.table;
|
package docking.widgets.table;
|
||||||
|
|
||||||
import static docking.DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
import static docking.DockingUtils.*;
|
||||||
import static docking.action.MenuData.NO_MNEMONIC;
|
import static docking.action.MenuData.*;
|
||||||
import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
|
import static java.awt.event.InputEvent.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
@ -448,8 +448,15 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the keyboard actions to pass through this table
|
* Enables the keyboard actions to pass through this table and up the component hierarchy.
|
||||||
* and up the component hierarchy.
|
* Specifically, passing true to this method allows unmodified keystrokes to work
|
||||||
|
* in the tool when this table is focused. Modified keystrokes, like <code>
|
||||||
|
* Ctrl-C</code>, will work at all times. Finally, if true is passed to this
|
||||||
|
* method, then the {@link #setAutoLookupColumn(int) auto lookup} feature is
|
||||||
|
* disabled.
|
||||||
|
*
|
||||||
|
* <p>The default state is for actions to be disabled.
|
||||||
|
*
|
||||||
* @param b true allows keyboard actions to pass up the component hierarchy.
|
* @param b true allows keyboard actions to pass up the component hierarchy.
|
||||||
*/
|
*/
|
||||||
public void setActionsEnabled(boolean b) {
|
public void setActionsEnabled(boolean b) {
|
||||||
|
@ -1496,7 +1503,8 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract static class GTableAction extends DockingAction {
|
private abstract static class GTableAction extends DockingAction
|
||||||
|
implements ComponentBasedDockingAction {
|
||||||
|
|
||||||
GTableAction(String name, String owner) {
|
GTableAction(String name, String owner) {
|
||||||
super(name, owner);
|
super(name, owner);
|
||||||
|
@ -1516,5 +1524,11 @@ public class GTable extends JTable implements KeyStrokeConsumer {
|
||||||
Component sourceComponent = context.getSourceComponent();
|
Component sourceComponent = context.getSourceComponent();
|
||||||
return sourceComponent instanceof GTable;
|
return sourceComponent instanceof GTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidComponentContext(ActionContext context) {
|
||||||
|
Component sourceComponent = context.getSourceComponent();
|
||||||
|
return sourceComponent instanceof GTable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.block;
|
package ghidra.program.model.block;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Listing;
|
import ghidra.program.model.listing.Listing;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -24,8 +25,6 @@ import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <CODE>MultEntSubModel</CODE> (M-model) defines subroutines which do not share code with
|
* <CODE>MultEntSubModel</CODE> (M-model) defines subroutines which do not share code with
|
||||||
* any other subroutine and may have one or more entry points. Each entry-
|
* any other subroutine and may have one or more entry points. Each entry-
|
||||||
|
@ -42,112 +41,117 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
|
|
||||||
public static final String NAME = "Multiple Entry";
|
public static final String NAME = "Multiple Entry";
|
||||||
|
|
||||||
protected Program program;
|
protected Program program;
|
||||||
protected Listing listing;
|
protected Listing listing;
|
||||||
|
|
||||||
private AddressObjectMap foundMSubs; // used for caching model-M subroutines
|
private AddressObjectMap foundMSubs; // used for caching model-M subroutines
|
||||||
private CodeBlockModel bbModel; // basic block model
|
private CodeBlockModel bbModel; // basic block model
|
||||||
protected final boolean includeExternals;
|
protected final boolean includeExternals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a <CODE>MultEntSubModel</CODE> for a program.
|
* Construct a <CODE>MultEntSubModel</CODE> for a program.
|
||||||
*
|
*
|
||||||
* @param program program to create blocks from.
|
* @param program program to create blocks from.
|
||||||
*/
|
*/
|
||||||
public MultEntSubModel(Program program) {
|
public MultEntSubModel(Program program) {
|
||||||
this(program, false);
|
this(program, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a <CODE>MultEntSubModel</CODE> for a program.
|
* Construct a <CODE>MultEntSubModel</CODE> for a program.
|
||||||
* @param program program to create blocks from.
|
* @param program program to create blocks from.
|
||||||
* @param includeExternals external blocks will be included if true
|
* @param includeExternals external blocks will be included if true
|
||||||
*/
|
*/
|
||||||
public MultEntSubModel(Program program, boolean includeExternals) {
|
public MultEntSubModel(Program program, boolean includeExternals) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.includeExternals = includeExternals;
|
this.includeExternals = includeExternals;
|
||||||
listing = program.getListing();
|
listing = program.getListing();
|
||||||
foundMSubs = new AddressObjectMap();
|
foundMSubs = new AddressObjectMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the code block that has an entry point at addr.
|
* Get the code block that has an entry point at addr.
|
||||||
*
|
*
|
||||||
* @param addr one of the entry points for a Model-M subroutine
|
* @param addr one of the entry points for a Model-M subroutine
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
* @return null if there is no subroutine with an entry at addr.
|
* @return null if there is no subroutine with an entry at addr.
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
*/
|
*/
|
||||||
public CodeBlock getCodeBlockAt(Address addr, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlock getCodeBlockAt(Address addr, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
if (addr == null)
|
if (addr == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
CodeBlock block = getSubFromCache(addr);
|
CodeBlock block = getSubFromCache(addr);
|
||||||
if (block == null) {
|
if (block == null) {
|
||||||
block = getAddressSetContaining(addr, monitor);
|
block = getAddressSetContaining(addr, monitor);
|
||||||
}
|
}
|
||||||
if (block != null){
|
if (block != null) {
|
||||||
Address[] entPts = block.getStartAddresses();
|
Address[] entPts = block.getStartAddresses();
|
||||||
for (int i = 0; i < entPts.length; i++) {
|
for (Address entPt : entPts) {
|
||||||
if (entPts[i].equals(addr))
|
if (entPt.equals(addr)) {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the M-Model subroutine address set which contains the specified address.
|
* Get the M-Model subroutine address set which contains the specified address.
|
||||||
* This method also identifies the entry points and caches the resulting CodeBlock.
|
* This method also identifies the entry points and caches the resulting CodeBlock.
|
||||||
*
|
*
|
||||||
* @param addr Address inside the subroutine that we are seeking
|
* @param addr Address inside the subroutine that we are seeking
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
* @return The <CODE>CodeBlock</CODE> for a
|
* @return The <CODE>CodeBlock</CODE> for a
|
||||||
* <CODE>MultEntSubModel</CODE> Subroutine.
|
* <CODE>MultEntSubModel</CODE> Subroutine.
|
||||||
* Null is returned if there is no instruction at addr.
|
* Null is returned if there is no instruction at addr.
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
**/
|
**/
|
||||||
protected CodeBlock getAddressSetContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
protected CodeBlock getAddressSetContaining(Address addr, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
if (addr.isExternalAddress()) {
|
if (addr.isExternalAddress()) {
|
||||||
if (includeExternals) {
|
if (includeExternals) {
|
||||||
CodeBlock block = new ExtCodeBlockImpl(this, addr);
|
CodeBlock block = new ExtCodeBlockImpl(this, addr);
|
||||||
foundMSubs.addObject(block, addr, addr);
|
foundMSubs.addObject(block, addr, addr);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a holder for the blockSet
|
// create a holder for the blockSet
|
||||||
AddressSet addrSet = new AddressSet();
|
AddressSet addrSet = new AddressSet();
|
||||||
|
|
||||||
// Use a new block model each time, so that only the current
|
// Use a new block model each time, so that only the current
|
||||||
// function basic blocks will get saved.
|
// function basic blocks will get saved.
|
||||||
bbModel = new SimpleBlockModel(program, includeExternals);
|
bbModel = new SimpleBlockModel(program, includeExternals);
|
||||||
|
|
||||||
// Create the todoStack and initialize it with instr; also initialize the list for entryPts.
|
// Create the todoStack and initialize it with instr; also initialize the list for entryPts.
|
||||||
ArrayList<Address> entryPtList = new ArrayList<Address>();
|
ArrayList<Address> entryPtList = new ArrayList<Address>();
|
||||||
LinkedList<Address> todoList = new LinkedList<Address>(); // list of address destinations to follow
|
LinkedList<Address> todoList = new LinkedList<Address>(); // list of address destinations to follow
|
||||||
LinkedList<CodeBlock> srcList = new LinkedList<CodeBlock>(); // list of blocks to process for possible sources
|
LinkedList<CodeBlock> srcList = new LinkedList<CodeBlock>(); // list of blocks to process for possible sources
|
||||||
todoList.addFirst(addr);
|
todoList.addFirst(addr);
|
||||||
|
|
||||||
// Build model-M subroutine from basic blocks
|
// Build model-M subroutine from basic blocks
|
||||||
while (!todoList.isEmpty() || !srcList.isEmpty()) {
|
while (!todoList.isEmpty() || !srcList.isEmpty()) {
|
||||||
|
|
||||||
if (monitor != null && monitor.isCancelled())
|
if (monitor != null && monitor.isCancelled()) {
|
||||||
throw new CancelledException();
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
// if todoList is empty
|
// if todoList is empty
|
||||||
// process any blocks for sources that we put off to analyze later
|
// process any blocks for sources that we put off to analyze later
|
||||||
// It is easier/efficient to follow flow in the forward direction.
|
// It is easier/efficient to follow flow in the forward direction.
|
||||||
if (todoList.isEmpty()) {
|
if (todoList.isEmpty()) {
|
||||||
while (todoList.isEmpty() && !srcList.isEmpty()) {
|
while (todoList.isEmpty() && !srcList.isEmpty()) {
|
||||||
CodeBlock bblock = srcList.removeFirst();
|
CodeBlock bblock = srcList.removeFirst();
|
||||||
// Process all block source references
|
// Process all block source references
|
||||||
addSources(monitor, entryPtList, todoList, bblock);
|
addSources(monitor, entryPtList, todoList, bblock);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the next address to process
|
// get the next address to process
|
||||||
|
@ -155,46 +159,50 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
a = todoList.removeFirst();
|
a = todoList.removeFirst();
|
||||||
|
|
||||||
// Get basic block at the specified address
|
// Get basic block at the specified address
|
||||||
if (addrSet.contains(a))
|
if (addrSet.contains(a)) {
|
||||||
continue; // already processed this block
|
continue; // already processed this block
|
||||||
|
}
|
||||||
|
|
||||||
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
|
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
|
||||||
if (bblock == null)
|
if (bblock == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that the block contains instructions
|
// Verify that the block contains instructions
|
||||||
if (listing.getInstructionAt(bblock.getMinAddress()) == null)
|
if (listing.getInstructionAt(bblock.getMinAddress()) == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Add basic block to subroutine address set
|
// Add basic block to subroutine address set
|
||||||
addrSet.add(bblock);
|
addrSet.add(bblock);
|
||||||
|
|
||||||
// Process all destination references
|
// Process all destination references
|
||||||
addDestinations(monitor, todoList, bblock);
|
addDestinations(monitor, todoList, bblock);
|
||||||
|
|
||||||
// add block to list of block to process later
|
// add block to list of block to process later
|
||||||
srcList.addLast(bblock);
|
srcList.addLast(bblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addrSet.isEmpty())
|
if (addrSet.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for failure to find entry point
|
// Check for failure to find entry point
|
||||||
if (entryPtList.size() == 0) {
|
if (entryPtList.size() == 0) {
|
||||||
Msg.warn(this, "WARNING: failed to find entry point for subroutine containing " + addr);
|
Msg.warn(this, "Failed to find entry point for subroutine containing " + addr);
|
||||||
entryPtList.add(addrSet.getMinAddress());
|
entryPtList.add(addrSet.getMinAddress());
|
||||||
}
|
}
|
||||||
Address[] entryPts = new Address[entryPtList.size()];
|
Address[] entryPts = new Address[entryPtList.size()];
|
||||||
entryPtList.toArray(entryPts);
|
entryPtList.toArray(entryPts);
|
||||||
|
|
||||||
CodeBlock block = new CodeBlockImpl(this, entryPts, addrSet);
|
CodeBlock block = new CodeBlockImpl(this, entryPts, addrSet);
|
||||||
foundMSubs.addObject(block, addrSet);
|
foundMSubs.addObject(block, addrSet);
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDestinations(TaskMonitor monitor, LinkedList<Address> todoList, CodeBlock bblock)
|
private void addDestinations(TaskMonitor monitor, LinkedList<Address> todoList,
|
||||||
throws CancelledException {
|
CodeBlock bblock) throws CancelledException {
|
||||||
CodeBlockReferenceIterator destIter = bblock.getDestinations(monitor);
|
CodeBlockReferenceIterator destIter = bblock.getDestinations(monitor);
|
||||||
while (destIter.hasNext()) {
|
while (destIter.hasNext()) {
|
||||||
CodeBlockReference destRef = destIter.next();
|
CodeBlockReference destRef = destIter.next();
|
||||||
|
@ -203,8 +211,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
if (destAddr.isMemoryAddress()) {
|
if (destAddr.isMemoryAddress()) {
|
||||||
FlowType refFlowType = destRef.getFlowType();
|
FlowType refFlowType = destRef.getFlowType();
|
||||||
if (refFlowType.isJump()) {
|
if (refFlowType.isJump()) {
|
||||||
todoList.addLast(destAddr);
|
todoList.addLast(destAddr);
|
||||||
}
|
}
|
||||||
else if (refFlowType.isFallthrough()) {
|
else if (refFlowType.isFallthrough()) {
|
||||||
todoList.addFirst(destAddr);
|
todoList.addFirst(destAddr);
|
||||||
}
|
}
|
||||||
|
@ -212,9 +220,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSources(TaskMonitor monitor,
|
private void addSources(TaskMonitor monitor, List<Address> entryPtList,
|
||||||
List<Address> entryPtList, LinkedList<Address> todoList, CodeBlock bblock)
|
LinkedList<Address> todoList, CodeBlock bblock) throws CancelledException {
|
||||||
throws CancelledException {
|
|
||||||
CodeBlockReferenceIterator srcIter = bblock.getSources(monitor);
|
CodeBlockReferenceIterator srcIter = bblock.getSources(monitor);
|
||||||
boolean isSource = true;
|
boolean isSource = true;
|
||||||
boolean isEntry = false;
|
boolean isEntry = false;
|
||||||
|
@ -225,7 +232,8 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
if (refFlowType.isJump() || refFlowType.isFallthrough()) {
|
if (refFlowType.isJump() || refFlowType.isFallthrough()) {
|
||||||
// Add Jump and Fall-through sources to the todoList
|
// Add Jump and Fall-through sources to the todoList
|
||||||
todoList.addLast(srcRef.getSourceAddress());
|
todoList.addLast(srcRef.getSourceAddress());
|
||||||
} else if (refFlowType.isCall()) {
|
}
|
||||||
|
else if (refFlowType.isCall()) {
|
||||||
// Basic block is a subroutine entry point
|
// Basic block is a subroutine entry point
|
||||||
isEntry = true;
|
isEntry = true;
|
||||||
}
|
}
|
||||||
|
@ -235,213 +243,233 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the MultEntSubModel Code Block that contains the address.
|
* Get the MultEntSubModel Code Block that contains the address.
|
||||||
*
|
*
|
||||||
* @param addr Address to find a containing block.
|
* @param addr Address to find a containing block.
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
* @return A CodeBlock if any block contains the address.
|
* @return A CodeBlock if any block contains the address.
|
||||||
* null otherwise.
|
* null otherwise.
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
*/
|
*/
|
||||||
public CodeBlock getFirstCodeBlockContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlock getFirstCodeBlockContaining(Address addr, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
CodeBlock block = getSubFromCache(addr);
|
CodeBlock block = getSubFromCache(addr);
|
||||||
if (block == null) {
|
if (block == null) {
|
||||||
block = getAddressSetContaining(addr, monitor);
|
block = getAddressSetContaining(addr, monitor);
|
||||||
}
|
}
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the one code block contained by addr (only for
|
||||||
|
* a model that has shared subroutines would this method
|
||||||
|
* return more than one code block)
|
||||||
|
*
|
||||||
|
* @param addr Address to find a containing block.
|
||||||
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
|
* @return A CodeBlock if any block contains the address.
|
||||||
|
* empty array otherwise.
|
||||||
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CodeBlock[] getCodeBlocksContaining(Address addr, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
CodeBlock sub = getFirstCodeBlockContaining(addr, monitor);
|
||||||
|
if (sub == null) {
|
||||||
|
return emptyBlockArray;
|
||||||
|
}
|
||||||
|
CodeBlock[] blocks = new CodeBlock[1];
|
||||||
|
blocks[0] = sub;
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the one code block contained by addr (only for
|
* Get an iterator over the code blocks in the entire program.
|
||||||
* a model that has shared subroutines would this method
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
* return more than one code block)
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
*
|
*/
|
||||||
* @param addr Address to find a containing block.
|
@Override
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
public CodeBlockIterator getCodeBlocks(TaskMonitor monitor) throws CancelledException {
|
||||||
* @return A CodeBlock if any block contains the address.
|
return new MultEntSubIterator(this, monitor);
|
||||||
* empty array otherwise.
|
}
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
|
||||||
*/
|
|
||||||
public CodeBlock[] getCodeBlocksContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
|
||||||
CodeBlock sub = getFirstCodeBlockContaining(addr, monitor);
|
|
||||||
if (sub == null) {
|
|
||||||
return emptyBlockArray;
|
|
||||||
}
|
|
||||||
CodeBlock[] blocks = new CodeBlock[1];
|
|
||||||
blocks[0] = sub;
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator over CodeBlocks which overlap the specified address set.
|
||||||
|
*
|
||||||
|
* @param addrSet an address set within program
|
||||||
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CodeBlockIterator getCodeBlocksContaining(AddressSetView addrSet, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
return new MultEntSubIterator(this, addrSet, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an iterator over the code blocks in the entire program.
|
* @see ghidra.program.model.block.CodeBlockModel#getProgram()
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
*/
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
@Override
|
||||||
*/
|
public Program getProgram() {
|
||||||
public CodeBlockIterator getCodeBlocks(TaskMonitor monitor) throws CancelledException {
|
return program;
|
||||||
return new MultEntSubIterator(this, monitor);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an iterator over CodeBlocks which overlap the specified address set.
|
* Returns the listing associated with this block model.
|
||||||
*
|
* @return the listing associated with this block model.
|
||||||
* @param addrSet an address set within program
|
*/
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
public Listing getListing() {
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
return listing;
|
||||||
*/
|
}
|
||||||
public CodeBlockIterator getCodeBlocksContaining(AddressSetView addrSet, TaskMonitor monitor) throws CancelledException {
|
|
||||||
return new MultEntSubIterator(this, addrSet, monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getProgram()
|
* @see ghidra.program.model.block.CodeBlockModel#getName(ghidra.program.model.block.CodeBlock)
|
||||||
*/
|
*/
|
||||||
public Program getProgram() {
|
@Override
|
||||||
return program;
|
public String getName(CodeBlock block) {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
* Returns the listing associated with this block model.
|
throw new IllegalArgumentException();
|
||||||
* @return the listing associated with this block model.
|
}
|
||||||
*/
|
|
||||||
public Listing getListing() {
|
|
||||||
return listing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// get the start address for the block
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getName(ghidra.program.model.block.CodeBlock)
|
// look up the symbol in the symbol table.
|
||||||
*/
|
// it should have one if anyone calls it.
|
||||||
public String getName(CodeBlock block) {
|
// if not, make up a label
|
||||||
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
Address start = block.getFirstStartAddress();
|
||||||
throw new IllegalArgumentException();
|
|
||||||
|
|
||||||
// get the start address for the block
|
Symbol symbol = program.getSymbolTable().getPrimarySymbol(start);
|
||||||
// look up the symbol in the symbol table.
|
if (symbol != null) {
|
||||||
// it should have one if anyone calls it.
|
return symbol.getName();
|
||||||
// if not, make up a label
|
}
|
||||||
|
|
||||||
Address start = block.getFirstStartAddress();
|
return "SOURCE_SUB" + start.toString();
|
||||||
|
}
|
||||||
|
|
||||||
Symbol symbol = program.getSymbolTable().getPrimarySymbol(start);
|
/**
|
||||||
if (symbol != null) {
|
* Return in general how things flow out of this node.
|
||||||
return symbol.getName();
|
* This method exists for the SIMPLEBLOCK model.
|
||||||
}
|
*
|
||||||
|
* <p>
|
||||||
|
* Since it doesn't make a great deal of sense to ask for this method
|
||||||
|
* in the case of subroutines, we return FlowType.UNKNOWN
|
||||||
|
* as long as the block exists.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If this block has no valid instructions, it can't flow,
|
||||||
|
* so FlowType.INVALID is returned.
|
||||||
|
*
|
||||||
|
* @return flow type of this node
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public FlowType getFlowType(CodeBlock block) {
|
||||||
|
|
||||||
return "SOURCE_SUB" + start.toString();
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
}
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there are multiple unique ways out of the node, then we
|
||||||
|
should return FlowType.UNKNOWN (or FlowType.MULTIFLOW ?).
|
||||||
|
Possible considerations for the future which are particularly
|
||||||
|
applicable to model-P subroutines: add FlowType.MULTICALL if
|
||||||
|
only calls out and FlowType.MULTIJUMP if multiple jumps OUT
|
||||||
|
(as opposed to jumping within the subroutine).
|
||||||
|
Might want to consider FlowType.MULTITERMINAL for multiple returns? */
|
||||||
|
|
||||||
/**
|
return RefType.FLOW;
|
||||||
* Return in general how things flow out of this node.
|
}
|
||||||
* This method exists for the SIMPLEBLOCK model.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Since it doesn't make a great deal of sense to ask for this method
|
|
||||||
* in the case of subroutines, we return FlowType.UNKNOWN
|
|
||||||
* as long as the block exists.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* If this block has no valid instructions, it can't flow,
|
|
||||||
* so FlowType.INVALID is returned.
|
|
||||||
*
|
|
||||||
* @return flow type of this node
|
|
||||||
*/
|
|
||||||
public FlowType getFlowType(CodeBlock block) {
|
|
||||||
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
/**
|
||||||
throw new IllegalArgumentException();
|
* @see ghidra.program.model.block.CodeBlockModel#getSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CodeBlockReferenceIterator getSources(CodeBlock block, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
/* If there are multiple unique ways out of the node, then we
|
return new SubroutineSourceReferenceIterator(block, monitor);
|
||||||
should return FlowType.UNKNOWN (or FlowType.MULTIFLOW ?).
|
}
|
||||||
Possible considerations for the future which are particularly
|
|
||||||
applicable to model-P subroutines: add FlowType.MULTICALL if
|
|
||||||
only calls out and FlowType.MULTIJUMP if multiple jumps OUT
|
|
||||||
(as opposed to jumping within the subroutine).
|
|
||||||
Might want to consider FlowType.MULTITERMINAL for multiple returns? */
|
|
||||||
|
|
||||||
return RefType.FLOW;
|
/**
|
||||||
}
|
* @see ghidra.program.model.block.CodeBlockModel#getNumSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getNumSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
/**
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
throw new IllegalArgumentException();
|
||||||
*/
|
}
|
||||||
public CodeBlockReferenceIterator getSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
|
|
||||||
return new SubroutineSourceReferenceIterator(block, monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getNumSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
|
||||||
*/
|
|
||||||
public int getNumSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
|
||||||
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
|
|
||||||
return SubroutineSourceReferenceIterator.getNumSources(block, monitor);
|
return SubroutineSourceReferenceIterator.getNumSources(block, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlockReferenceIterator getDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlockReferenceIterator getDestinations(CodeBlock block, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return new SubroutineDestReferenceIterator(block, monitor);
|
return new SubroutineDestReferenceIterator(block, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get number of destination references flowing out of this subroutine (block).
|
* Get number of destination references flowing out of this subroutine (block).
|
||||||
* All Calls from this block, and all external FlowType block references
|
* All Calls from this block, and all external FlowType block references
|
||||||
* from this block are counted.
|
* from this block are counted.
|
||||||
*
|
*
|
||||||
* @param block code block to get the number of destination references from.
|
* @param block code block to get the number of destination references from.
|
||||||
* @param monitor task monitor which allows user to cancel operation.
|
* @param monitor task monitor which allows user to cancel operation.
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
*/
|
*/
|
||||||
public int getNumDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public int getNumDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return SubroutineDestReferenceIterator.getNumDestinations(block, monitor);
|
return SubroutineDestReferenceIterator.getNumDestinations(block, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute an address set that represents all the addresses contained
|
* Compute an address set that represents all the addresses contained
|
||||||
* in all instructions that are part of this block
|
* in all instructions that are part of this block
|
||||||
*
|
*
|
||||||
* @param block code block to compute address set for.
|
* @param block code block to compute address set for.
|
||||||
*/
|
*/
|
||||||
public AddressSetView getAddressSet(CodeBlock block) {
|
public AddressSetView getAddressSet(CodeBlock block) {
|
||||||
|
|
||||||
if (!(block.getModel() instanceof MultEntSubModel))
|
if (!(block.getModel() instanceof MultEntSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return new AddressSet(block);
|
return new AddressSet(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a subroutine from the cache containing addr. If none there, returns null.
|
|
||||||
* It is assumed that an address will only occur within a single MSub
|
|
||||||
*/
|
|
||||||
private CodeBlock getSubFromCache(Address addr){
|
|
||||||
Object[] mapObjs = this.foundMSubs.getObjects(addr);
|
|
||||||
return mapObjs.length == 0 ? null : (CodeBlock) mapObjs[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a subroutine from the cache containing addr. If none there, returns null.
|
||||||
|
* It is assumed that an address will only occur within a single MSub
|
||||||
|
*/
|
||||||
|
private CodeBlock getSubFromCache(Address addr) {
|
||||||
|
Object[] mapObjs = this.foundMSubs.getObjects(addr);
|
||||||
|
return mapObjs.length == 0 ? null : (CodeBlock) mapObjs[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getBasicBlockModel()
|
* @see ghidra.program.model.block.CodeBlockModel#getBasicBlockModel()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public CodeBlockModel getBasicBlockModel() {
|
public CodeBlockModel getBasicBlockModel() {
|
||||||
if (bbModel == null) {
|
if (bbModel == null) {
|
||||||
bbModel = new SimpleBlockModel(program);
|
bbModel = new SimpleBlockModel(program);
|
||||||
|
@ -452,6 +480,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getName()
|
* @see ghidra.program.model.block.CodeBlockModel#getName()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
@ -459,6 +488,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.SubroutineBlockModel#getBaseSubroutineModel()
|
* @see ghidra.program.model.block.SubroutineBlockModel#getBaseSubroutineModel()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public SubroutineBlockModel getBaseSubroutineModel() {
|
public SubroutineBlockModel getBaseSubroutineModel() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -466,6 +496,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#allowsBlockOverlap()
|
* @see ghidra.program.model.block.CodeBlockModel#allowsBlockOverlap()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean allowsBlockOverlap() {
|
public boolean allowsBlockOverlap() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -473,6 +504,7 @@ public class MultEntSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#externalsIncluded()
|
* @see ghidra.program.model.block.CodeBlockModel#externalsIncluded()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean externalsIncluded() {
|
public boolean externalsIncluded() {
|
||||||
return includeExternals;
|
return includeExternals;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue