mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-3227 - review fixes
This commit is contained in:
parent
665a83d6c0
commit
c4b6058c31
4 changed files with 185 additions and 279 deletions
|
@ -15,13 +15,20 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.equate;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.database.symbol.EquateManager;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.Equate;
|
||||
import ghidra.program.model.symbol.EquateTable;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class CreateEnumEquateCommand extends BackgroundCommand {
|
||||
|
@ -30,6 +37,7 @@ public class CreateEnumEquateCommand extends BackgroundCommand {
|
|||
private Enum enoom;
|
||||
private Program program;
|
||||
private boolean shouldDoOnSubOps;
|
||||
private EquateTable equateTable;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -41,18 +49,23 @@ public class CreateEnumEquateCommand extends BackgroundCommand {
|
|||
*/
|
||||
public CreateEnumEquateCommand(Program program, AddressSetView addresses, Enum enoom,
|
||||
boolean shouldDoOnSubOps) {
|
||||
this.program = program;
|
||||
this.addresses = addresses;
|
||||
this.enoom = enoom;
|
||||
this.program = Objects.requireNonNull(program);
|
||||
this.addresses = Objects.requireNonNull(addresses);
|
||||
this.enoom = Objects.requireNonNull(enoom);
|
||||
this.shouldDoOnSubOps = shouldDoOnSubOps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Create Enum Equate Command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
|
||||
EquateTable et = program.getEquateTable();
|
||||
equateTable = program.getEquateTable();
|
||||
try {
|
||||
et.applyEnum(addresses, enoom, monitor, shouldDoOnSubOps);
|
||||
applyEnum(monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return false;
|
||||
|
@ -60,8 +73,95 @@ public class CreateEnumEquateCommand extends BackgroundCommand {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Create Enum Equate Command";
|
||||
public void applyEnum(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
InstructionIterator it = listing.getInstructions(addresses, true);
|
||||
monitor.initialize(addresses.getNumAddresses());
|
||||
while (it.hasNext()) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
Instruction instruction = it.next();
|
||||
processsEquates(instruction);
|
||||
monitor.incrementProgress(instruction.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
private void processsEquates(Instruction instruction) {
|
||||
for (int opIndex = 0; opIndex < instruction.getNumOperands(); opIndex++) {
|
||||
|
||||
if (!shouldDoOnSubOps) {
|
||||
// Only apply equates to scalars that are not contained in sub operands.
|
||||
Scalar scalar = instruction.getScalar(opIndex);
|
||||
maybeCreateEquateOnScalar(instruction, opIndex, scalar);
|
||||
}
|
||||
else {
|
||||
// Apply equates to scalars in the sub operands as well.
|
||||
List<?> subOperands = instruction.getDefaultOperandRepresentationList(opIndex);
|
||||
for (Object subOp : subOperands) {
|
||||
maybeCreateEquateOnScalar(instruction, opIndex, subOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeCreateEquateOnScalar(Instruction instruction, int opIndex,
|
||||
Object operandRepresentation) {
|
||||
|
||||
if (!(operandRepresentation instanceof Scalar)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar scalar = (Scalar) operandRepresentation;
|
||||
|
||||
int enoomLength = enoom.getLength();
|
||||
boolean anyValuesMatch = Arrays.stream(enoom.getValues()).anyMatch(enumValue -> {
|
||||
return scalar.equals(new Scalar(enoomLength * 8, enumValue, scalar.isSigned()));
|
||||
});
|
||||
|
||||
if (!anyValuesMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (program.getDataTypeManager().findDataTypeForID(enoom.getUniversalID()) == null) {
|
||||
enoom = (Enum) program.getDataTypeManager().addDataType(enoom, null);
|
||||
}
|
||||
|
||||
Address addr = instruction.getAddress();
|
||||
removeUnusedEquates(opIndex, scalar, addr);
|
||||
|
||||
long value = scalar.getValue();
|
||||
String equateName = EquateManager.formatNameForEquate(enoom.getUniversalID(), value);
|
||||
Equate equate = getOrCreateEquate(equateName, value);
|
||||
equate.addReference(addr, opIndex);
|
||||
}
|
||||
|
||||
private void removeUnusedEquates(int opIndex, Scalar scalar, Address addr) {
|
||||
Equate existingEquate = equateTable.getEquate(addr, opIndex, scalar.getValue());
|
||||
if (existingEquate != null) {
|
||||
if (existingEquate.getReferenceCount() <= 1) {
|
||||
equateTable.removeEquate(existingEquate.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Equate getOrCreateEquate(String name, long value) {
|
||||
Equate equate = equateTable.getEquate(name);
|
||||
if (equate != null) {
|
||||
return equate;
|
||||
}
|
||||
|
||||
try {
|
||||
equate = equateTable.createEquate(name, value);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException e) {
|
||||
// These should not happen:
|
||||
// Duplicate will not happen since we checked for the existence first; Invalid
|
||||
// can't happen since we built the name ourselves (we are assuming)
|
||||
Msg.error(this, "Unexpected error creating equate", e); // just in case
|
||||
}
|
||||
return equate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,16 +38,13 @@ import ghidra.program.model.data.*;
|
|||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.InvalidNameException;
|
||||
|
||||
/**
|
||||
* Tests for the make enum from a selection of enums action
|
||||
* Tests for the 'make enum from a selection' action
|
||||
*/
|
||||
public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private static final String PROGRAM_FILENAME = "notepad";
|
||||
private static final int TASK_TIMEOUT = 2000;
|
||||
|
||||
//private Program program;
|
||||
private PluginTool tool;
|
||||
private ProgramDB program;
|
||||
private TestEnv env;
|
||||
|
@ -57,10 +54,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
private ArchiveRootNode archiveRootNode;
|
||||
private ArchiveNode programNode;
|
||||
|
||||
public CreateEnumFromSelectionTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv();
|
||||
|
@ -93,16 +86,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
return builder.getProgram();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void tearDown() throws Exception {
|
||||
// executeOnSwingWithoutBlocking(new Runnable() {
|
||||
// public void run() {
|
||||
// ProgramManager pm = tool.getService(ProgramManager.class);
|
||||
// pm.closeProgram();
|
||||
//
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
|
||||
|
@ -116,8 +99,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
});
|
||||
|
||||
// this handles the save changes dialog and potential analysis dialogs
|
||||
closeAllWindowsAndFrames();
|
||||
env.release(program);
|
||||
closeAllWindows();
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
|
@ -174,7 +156,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
}
|
||||
});
|
||||
|
||||
Window window = waitForWindow(tool.getToolFrame(), "Name new ENUM", TASK_TIMEOUT);
|
||||
Window window = waitForWindow("Name new ENUM");
|
||||
assertNotNull(window);
|
||||
|
||||
final JTextField tf = findComponent(window, JTextField.class);
|
||||
|
@ -274,7 +256,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
}
|
||||
});
|
||||
|
||||
Window window = waitForWindow(tool.getToolFrame(), "Name new ENUM", TASK_TIMEOUT);
|
||||
Window window = waitForWindow("Name new ENUM");
|
||||
assertNotNull(window);
|
||||
|
||||
final JTextField tf = findComponent(window, JTextField.class);
|
||||
|
@ -283,7 +265,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
tf.setText("myNewEnum");
|
||||
pressButtonByText(window, "OK");
|
||||
|
||||
Window window2 = waitForWindow(tool.getToolFrame(), "Duplicate ENUM Name", TASK_TIMEOUT);
|
||||
Window window2 = waitForWindow("Duplicate ENUM Name");
|
||||
assertNotNull(window2);
|
||||
|
||||
final JTextField tf2 = findComponent(window2, JTextField.class);
|
||||
|
@ -334,27 +316,6 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
|
||||
}
|
||||
|
||||
private DataTypeNode createEnum(CategoryNode categoryNode, String newEnumName, int size) {
|
||||
Category category = categoryNode.getCategory();
|
||||
DataTypeManager dataTypeManager = category.getDataTypeManager();
|
||||
int id = dataTypeManager.startTransaction("new category");
|
||||
Enum newEnum = new EnumDataType(newEnumName, size);
|
||||
category.addDataType(newEnum, null);
|
||||
dataTypeManager.endTransaction(id, true);
|
||||
waitForTree();
|
||||
return getDataTypeNode(categoryNode, newEnumName);
|
||||
}
|
||||
|
||||
private DataTypeNode getDataTypeNode(GTreeNode parent, String dataTypeName) {
|
||||
List<GTreeNode> children = parent.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
if (node instanceof DataTypeNode && node.getName().equals(dataTypeName)) {
|
||||
return (DataTypeNode) node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void expandNode(GTreeNode node) {
|
||||
tree.expandPath(node);
|
||||
waitForTree();
|
||||
|
@ -373,37 +334,4 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||
private void waitForTree() {
|
||||
waitForTree(tree);
|
||||
}
|
||||
|
||||
private void waitForProgram() throws Exception {
|
||||
program.flushEvents();
|
||||
waitForTasks();
|
||||
waitForPostedSwingRunnables();
|
||||
}
|
||||
|
||||
private CategoryNode createCategory(CategoryNode categoryNode, String newCategoryName) {
|
||||
Category category = categoryNode.getCategory();
|
||||
DataTypeManager dataTypeManager = category.getDataTypeManager();
|
||||
int id = dataTypeManager.startTransaction("new category");
|
||||
try {
|
||||
category.createCategory(newCategoryName);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
// shouldn't happen
|
||||
}
|
||||
dataTypeManager.endTransaction(id, true);
|
||||
waitForTree();
|
||||
|
||||
CategoryNode node = getCategoryNode(categoryNode, newCategoryName);
|
||||
return node;
|
||||
}
|
||||
|
||||
private CategoryNode getCategoryNode(GTreeNode parent, String categoryName) {
|
||||
List<GTreeNode> children = parent.getChildren();
|
||||
for (GTreeNode node : children) {
|
||||
if (node instanceof CategoryNode && node.getName().equals(categoryName)) {
|
||||
return (CategoryNode) node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,6 @@ package ghidra.program.database.symbol;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import db.*;
|
||||
import db.util.ErrorHandler;
|
||||
|
@ -27,14 +24,12 @@ import ghidra.program.database.*;
|
|||
import ghidra.program.database.map.AddressKeyAddressIterator;
|
||||
import ghidra.program.database.map.AddressMap;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.Equate;
|
||||
import ghidra.program.model.symbol.EquateTable;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.program.util.EquateInfo;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Lock;
|
||||
import ghidra.util.UniversalID;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -114,112 +109,6 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB {
|
|||
program.dbError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyEnum(AddressSetView addresses, Enum enoom, TaskMonitor monitor,
|
||||
boolean shouldDoOnSubOps) throws CancelledException {
|
||||
|
||||
if (addresses == null) {
|
||||
throw new IllegalArgumentException("Can't apply Enum over null addresses");
|
||||
}
|
||||
|
||||
if (enoom == null) {
|
||||
throw new IllegalArgumentException("Data Type is null");
|
||||
}
|
||||
|
||||
Consumer<Instruction> applyEquates = instruction -> {
|
||||
|
||||
if (monitor.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int opIndex = 0; opIndex < instruction.getNumOperands(); opIndex++) {
|
||||
|
||||
if (!shouldDoOnSubOps) {
|
||||
// Only apply equates to scalars that are not contained in sub operands.
|
||||
Scalar scalar = instruction.getScalar(opIndex);
|
||||
maybeCreateEquateOnScalar(enoom, instruction, opIndex, scalar);
|
||||
}
|
||||
else {
|
||||
// Apply equates to scalars in the sub operands as well.
|
||||
List<?> subOperands = instruction.getDefaultOperandRepresentationList(opIndex);
|
||||
for (Object subOp : subOperands) {
|
||||
maybeCreateEquateOnScalar(enoom, instruction, opIndex, subOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Listing listing = program.getListing();
|
||||
InstructionIterator it = listing.getInstructions(addresses, true);
|
||||
Stream<Instruction> instructions = StreamSupport.stream(it.spliterator(), false);
|
||||
|
||||
try {
|
||||
lock.acquire();
|
||||
instructions.forEach(applyEquates);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeCreateEquateOnScalar(Enum enoom, Instruction instruction, int opIndex,
|
||||
Object operandRepresentation) {
|
||||
|
||||
if (!(operandRepresentation instanceof Scalar)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar scalar = (Scalar) operandRepresentation;
|
||||
|
||||
int enoomLength = enoom.getLength();
|
||||
boolean anyValuesMatch = Arrays.stream(enoom.getValues()).anyMatch(enumValue -> {
|
||||
return scalar.equals(new Scalar(enoomLength * 8, enumValue, scalar.isSigned()));
|
||||
});
|
||||
|
||||
if (!anyValuesMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (program.getDataTypeManager().findDataTypeForID(enoom.getUniversalID()) == null) {
|
||||
enoom = (Enum) program.getDataTypeManager().addDataType(enoom, null);
|
||||
}
|
||||
|
||||
Address addr = instruction.getAddress();
|
||||
removeUnusedEquates(opIndex, scalar, addr);
|
||||
|
||||
long value = scalar.getValue();
|
||||
String equateName = EquateManager.formatNameForEquate(enoom.getUniversalID(), value);
|
||||
Equate equate = getOrCreateEquate(equateName, value);
|
||||
equate.addReference(addr, opIndex);
|
||||
}
|
||||
|
||||
private void removeUnusedEquates(int opIndex, Scalar scalar, Address addr) {
|
||||
Equate existingEquate = getEquate(addr, opIndex, scalar.getValue());
|
||||
if (existingEquate != null) {
|
||||
if (existingEquate.getReferenceCount() <= 1) {
|
||||
removeEquate(existingEquate.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Equate getOrCreateEquate(String name, long value) {
|
||||
Equate equate = getEquate(name);
|
||||
if (equate != null) {
|
||||
return equate;
|
||||
}
|
||||
|
||||
try {
|
||||
equate = createEquate(name, value);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException e) {
|
||||
// These should not happen:
|
||||
// Duplicate will not happen since we checked for the existence first; Invalid
|
||||
// can't happen since we built the name ourselves (we are assuming)
|
||||
Msg.error(this, "Unexpected error creating equate", e); // just in case
|
||||
}
|
||||
return equate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Equate createEquate(String name, long value)
|
||||
throws DuplicateNameException, InvalidInputException {
|
||||
|
|
|
@ -21,9 +21,9 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* EquateTable manages all equates for program. An equate defines a relationship
|
||||
* between a scalar value and a string whereby the scalar may be represented by
|
||||
|
@ -32,35 +32,20 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
|
||||
public interface EquateTable {
|
||||
/**
|
||||
* Creates equates and/or adds references for scalars
|
||||
* in the given address set using the given data type.
|
||||
* The data type given must be an enumeration data type.
|
||||
* @param addrSet the address set to use.
|
||||
* @param dataType the data type to use.
|
||||
* @param monitor task monitor to cancel the remove operation
|
||||
* @param shouldDoOnSubOps true if the enum should be applied inside sub-operands as well.
|
||||
*
|
||||
* @throws CancelledException if the operation is cancelled
|
||||
* @throws IllegalArgumentException if the dataType is null or not an enum.
|
||||
*/
|
||||
public void applyEnum(AddressSetView addrSet, Enum dataType, TaskMonitor monitor,
|
||||
boolean shouldDoOnSubOps) throws CancelledException;
|
||||
|
||||
/**
|
||||
* Creates a new equate
|
||||
* @param name the name to associate with the given value.
|
||||
* @param value the value to associate with the given name.
|
||||
* @return the equate
|
||||
* @exception DuplicateNameException thrown if name is already in use
|
||||
* as an equate.
|
||||
* @throws InvalidInputException if name contains blank characters,
|
||||
* is zero length, or is null
|
||||
*/
|
||||
public Equate createEquate(String name,long value)
|
||||
public Equate createEquate(String name, long value)
|
||||
throws DuplicateNameException, InvalidInputException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes the equate from the program.
|
||||
* @param name the name of the equate to remove.
|
||||
|
@ -75,12 +60,13 @@ public interface EquateTable {
|
|||
* @param monitor task monitor to cancel the remove operation
|
||||
* @throws CancelledException if the operation was cancelled.
|
||||
*/
|
||||
public void deleteAddressRange(Address start, Address end, TaskMonitor monitor) throws CancelledException;
|
||||
public void deleteAddressRange(Address start, Address end, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
|
||||
/**
|
||||
* Returns the equate with the given name, null if no such equate
|
||||
* exists.
|
||||
* @param name the of the equate to be retrieved.
|
||||
* Returns the equate with the given name, null if no such equate exists
|
||||
* @param name the of the equate to be retrieved
|
||||
* @return the equate
|
||||
*/
|
||||
public Equate getEquate(String name);
|
||||
|
||||
|
@ -114,17 +100,20 @@ public interface EquateTable {
|
|||
/**
|
||||
* Returns an address iterator over all the addresses where
|
||||
* equates have been set.
|
||||
* @return the iterator
|
||||
*/
|
||||
public AddressIterator getEquateAddresses();
|
||||
|
||||
/**
|
||||
* Returns all equates defined for value.
|
||||
* @param value the value to get all equates for.
|
||||
* @return the equates
|
||||
*/
|
||||
public List<Equate> getEquates(long value);
|
||||
|
||||
/**
|
||||
* Returns an iterator over all equates.
|
||||
* @return the iterator
|
||||
*/
|
||||
public Iterator<Equate> getEquates();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue