Merge remote-tracking branch 'origin/master' into debugger

This commit is contained in:
Dan
2021-01-12 10:04:56 -05:00
88 changed files with 1940 additions and 1130 deletions
@@ -268,11 +268,11 @@ public class DataTypeMergeManager implements MergeResolver {
private void processSourceArchiveChanges() throws CancelledException {
conflictOption = OPTION_MY;
for (int i = 0; i < myArchiveChangeList.size(); i++) {
for (Long element : myArchiveChangeList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long id = myArchiveChangeList.get(i).longValue();
long id = element.longValue();
updateSourceArchive(id);
}
@@ -318,11 +318,11 @@ public class DataTypeMergeManager implements MergeResolver {
}
private void processSourceArchiveAdditions() throws CancelledException {
for (int i = 0; i < myArchiveAddedList.size(); i++) {
for (Long element : myArchiveAddedList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long id = myArchiveAddedList.get(i).longValue();
long id = element.longValue();
UniversalID universalID = new UniversalID(id);
SourceArchive mySourceArchive = dtms[MY].getSourceArchive(universalID);
@@ -381,11 +381,11 @@ public class DataTypeMergeManager implements MergeResolver {
private void processSourceArchiveConflicts() throws CancelledException {
for (int i = 0; i < archiveConflictList.size(); i++) {
for (Long element : archiveConflictList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long sourceArchiveID = archiveConflictList.get(i).longValue();
long sourceArchiveID = element.longValue();
++currentConflictIndex;
handleSourceArchiveConflict(sourceArchiveID, currentConflictIndex);
@@ -412,11 +412,11 @@ public class DataTypeMergeManager implements MergeResolver {
* @throws CancelledException if task cancelled
*/
private void processCategoriesAdded() throws CancelledException {
for (int i = 0; i < myCatAddedList.size(); i++) {
for (Long element : myCatAddedList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long id = myCatAddedList.get(i).longValue();
long id = element.longValue();
Category myCat = dtms[MY].getCategory(id);
CategoryPath myPath = myCat.getCategoryPath();
if (dtms[RESULT].containsCategory(myPath)) {
@@ -432,11 +432,11 @@ public class DataTypeMergeManager implements MergeResolver {
*/
private void processCategoryConflicts() throws CancelledException {
for (int i = 0; i < catConflictList.size(); i++) {
for (Long element : catConflictList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long id = catConflictList.get(i).longValue();
long id = element.longValue();
++currentConflictIndex;
handleCategoryConflict(id, currentConflictIndex);
@@ -460,11 +460,11 @@ public class DataTypeMergeManager implements MergeResolver {
}
private void processCategoryChanges() throws CancelledException {
for (int i = 0; i < myCatChangeList.size(); i++) {
for (Long element : myCatChangeList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long id = myCatChangeList.get(i).longValue();
long id = element.longValue();
processCategoryRenamed(id);
processCategoryMoved(id);
@@ -473,10 +473,10 @@ public class DataTypeMergeManager implements MergeResolver {
}
private void processCategoriesDeleted() throws CancelledException {
for (int i = 0; i < myCatChangeList.size(); i++) {
for (Long element : myCatChangeList) {
currentMonitor.checkCanceled();
long id = myCatChangeList.get(i).longValue();
long id = element.longValue();
processCategoryDeleted(id);
}
}
@@ -499,8 +499,7 @@ public class DataTypeMergeManager implements MergeResolver {
if (fixUpList.size() > 0) {
StringBuffer sb = new StringBuffer();
sb.append("The following data types are unresolved:\n");
for (int i = 0; i < fixUpList.size(); i++) {
FixUpInfo info = fixUpList.get(i);
for (FixUpInfo info : fixUpList) {
DataTypeManager dtm = info.getDataTypeManager();
DataType dt = dtm.getDataType(info.id);
DataType compDt = dtm.getDataType(info.compID);
@@ -1128,26 +1127,22 @@ public class DataTypeMergeManager implements MergeResolver {
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID, Integer.MAX_VALUE,
resolvedDataTypes));
fixUpIDSet.add(sourceDtID);
// substitute datatype to preserve component name for subsequent fixup
resultCompDt = Undefined1DataType.dataType;
}
}
try {
if (resultCompDt != null) {
// There is a matching component data type in the result.
try {
// If I have compDt, it should now be from result DTM.
destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(),
comment);
}
catch (IllegalArgumentException e) {
displayError(destStruct, e);
DataType badDt = Undefined1DataType.dataType;
comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " +
e.getMessage() + " " + ((comment != null) ? (" " + comment) : "");
destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment);
}
// Apply resultCompDt as flex array
try {
destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(), comment);
}
else {
destStruct.clearFlexibleArrayComponent();
catch (IllegalArgumentException e) {
displayError(destStruct, e);
DataType badDt = Undefined1DataType.dataType;
comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " +
e.getMessage() + " " + ((comment != null) ? (" " + comment) : "");
destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment);
}
}
catch (IllegalArgumentException e) {
@@ -1216,7 +1211,9 @@ public class DataTypeMergeManager implements MergeResolver {
}
else {
// must have been deleted in LATEST
// put an entry in the fixup list if this is a conflict
// put an entry in the fixup list if this is a conflict.
// NOTE: This may also be caused by a replaced datatype but
// we have no indication as to what the replacment was
deletedInLatest = true;
}
}
@@ -1599,11 +1596,11 @@ public class DataTypeMergeManager implements MergeResolver {
*/
private void processDataTypeChanges() throws CancelledException {
for (int i = 0; i < myDtChangeList.size(); i++) {
for (Long element : myDtChangeList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long id = myDtChangeList.get(i).longValue();
long id = element.longValue();
DataType dt = dtms[MY].getDataType(id);
if ((dt instanceof Pointer) || (dt instanceof Array) || (dt instanceof BuiltIn)) {
@@ -1681,8 +1678,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
private boolean isParent(CategoryPath catPath) {
for (int i = 0; i < myDtAddedList.size(); i++) {
Long id = myDtAddedList.get(i);
for (Long id : myDtAddedList) {
DataType dt = dtms[MY].getDataType(id.longValue());
if (catPath.equals(dt.getCategoryPath())) {
return true;
@@ -2225,10 +2221,10 @@ public class DataTypeMergeManager implements MergeResolver {
}
private void processDataTypesDeleted() throws CancelledException {
for (int i = 0; i < myDtChangeList.size(); i++) {
for (Long element : myDtChangeList) {
currentMonitor.checkCanceled();
long id = myDtChangeList.get(i).longValue();
long id = element.longValue();
processDataTypeDeleted(id);
}
}
@@ -2237,11 +2233,11 @@ public class DataTypeMergeManager implements MergeResolver {
*
*/
private void processDataTypesAdded() throws CancelledException {
for (int i = 0; i < myDtAddedList.size(); i++) {
for (Long element : myDtAddedList) {
currentMonitor.checkCanceled();
currentMonitor.setProgress(++progressIndex);
long myDtKey = myDtAddedList.get(i).longValue();
long myDtKey = element.longValue();
DataType myDt = dtms[MY].getDataType(myDtKey);
if (equivalentDataTypeFound(myDtKey, myDt)) {
@@ -3360,7 +3356,7 @@ public class DataTypeMergeManager implements MergeResolver {
if (nextOrdinal > maxOrdinal) {
break;
}
struct.getComponent(nextOrdinal);
dtc = struct.getComponent(nextOrdinal);
}
return null;
}
@@ -204,7 +204,7 @@ public class BitFieldEditorPanel extends JPanel {
private JComponent createDataTypeChoiceEditor() {
dtChoiceEditor =
new DataTypeSelectionEditor(dtmService, -1, AllowedDataTypes.BITFIELD_BASE_TYPE);
new DataTypeSelectionEditor(dtmService, AllowedDataTypes.BITFIELD_BASE_TYPE);
dtChoiceEditor.setConsumeEnterKeyPress(false);
dtChoiceEditor.setTabCommitsEdit(true);
//dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager());
@@ -539,7 +539,7 @@ public class BitFieldEditorPanel extends JPanel {
dtChoiceEditor.getDropDownTextField().setText("");
fieldNameTextField.setText(null);
fieldCommentTextField.setText(null);
;
bitOffsetModel.setValue(0L);
bitSizeModel.setValue(1L);
}
@@ -1255,7 +1255,7 @@ public abstract class CompositeEditorPanel extends JPanel
Plugin plugin = provider.getPlugin();
final PluginTool tool = plugin.getTool();
editor = new DataTypeSelectionEditor(tool, maxLength,
editor = new DataTypeSelectionEditor(tool,
bitfieldAllowed ? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD
: AllowedDataTypes.SIZABLE_DYNAMIC);
editor.setTabCommitsEdit(true);
@@ -1287,12 +1287,7 @@ public abstract class CompositeEditorPanel extends JPanel
}
};
dataTypeChooserButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Swing.runLater(() -> stopEdit(tool));
}
});
dataTypeChooserButton.addActionListener(e -> Swing.runLater(() -> stopEdit(tool)));
textField.addFocusListener(new FocusAdapter() {
@Override
@@ -24,12 +24,8 @@ import docking.action.*;
import ghidra.app.context.ListingActionContext;
import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.util.*;
import ghidra.util.SystemUtilities;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Data;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
/**
@@ -58,121 +54,21 @@ public class ChooseDataTypeAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
ListingActionContext programActionContext =
(ListingActionContext) context.getContextObject();
int maxSize = Integer.MAX_VALUE;
Program program = programActionContext.getProgram();
ProgramLocation loc = programActionContext.getLocation();
ProgramSelection sel = programActionContext.getSelection();
if (sel != null && !sel.isEmpty()) {
InteriorSelection interiorSel = sel.getInteriorSelection();
if (interiorSel != null) {
maxSize = getSizeInsideStructure(program, interiorSel);
}
else {
maxSize = getSizeForSelection(program, sel);
}
}
else {
int[] compPath = loc.getComponentPath();
if (compPath != null && compPath.length > 0) {
maxSize = getSizeInsideStructure(program, loc);
}
else {
maxSize = getSizeForAddress(program, loc);
}
}
// unable to create data types at the current location
if (maxSize < 0) {
return;
}
Pointer pointer = program.getDataTypeManager().getPointer(null);
DataType dataType = getDataType(programActionContext, maxSize, pointer.getLength());
ListingActionContext listingContext = (ListingActionContext) context.getContextObject();
DataType dataType = getDataType(listingContext);
if (dataType != null) {
plugin.doCreateData(program, loc, sel, dataType, false);
plugin.createData(dataType, listingContext, true);
}
}
private int getSizeInsideStructure(Program program, InteriorSelection selection) {
ProgramLocation location = selection.getFrom();
Data dataComponent = getParentDataType(program, location);
if (dataComponent == null) {
return -1;
}
return selection.getByteLength();
}
private int getSizeInsideStructure(Program program, ProgramLocation location) {
Data dataComponent = getParentDataType(program, location);
if (dataComponent == null) {
return -1;
}
return getMaxSizeInStructure((Structure) dataComponent.getParent().getBaseDataType(),
dataComponent.getComponentIndex());
}
private int getSizeForAddress(Program program, ProgramLocation location) {
Address address = location.getAddress();
Data data = program.getListing().getDataAt(address);
if (data == null) {
plugin.getTool().setStatusInfo("Create Data Failed! No data at " + address);
return -1;
}
return getMaxSize(program, address);
}
private Data getParentDataType(Program program, ProgramLocation location) {
int[] path = location.getComponentPath();
Address address = location.getAddress();
Data data = program.getListing().getDataContaining(address);
Data dataComponent = null;
if (data != null) {
dataComponent = data.getComponent(path);
}
if (dataComponent == null) {
plugin.getTool().setStatusInfo("Create data type failed! No data at " + address);
return null;
}
DataType parentDataType = dataComponent.getParent().getBaseDataType();
if (!(parentDataType instanceof Structure)) {
plugin.getTool().setStatusInfo("Cannot set data type here.");
return null;
}
return dataComponent;
}
private int getSizeForSelection(Program program, ProgramSelection selection) {
PluginTool tool = plugin.getTool();
AddressRange range = selection.getFirstRange();
Address address = selection.getMinAddress();
Data data = program.getListing().getDataAt(address);
if (data == null) {
tool.setStatusInfo("Cannot set data type! No data at " + address);
return -1;
}
return (int) range.getLength();
}
private DataType getDataType(ListingActionContext context, int maxElements,
int defaultPointerSize) {
private DataType getDataType(ListingActionContext context) {
PluginTool tool = plugin.getTool();
Data data = plugin.getDataUnit(context);
int noSizeRestriction = -1;
DataTypeSelectionDialog selectionDialog = new DataTypeSelectionDialog(tool,
data.getProgram().getDataTypeManager(), maxElements, AllowedDataTypes.ALL);
DataType currentDataType = data.getBaseDataType();
selectionDialog.setInitialDataType(currentDataType);
data.getProgram().getDataTypeManager(), noSizeRestriction, AllowedDataTypes.ALL);
DataType initialType = data.getBaseDataType();
selectionDialog.setInitialDataType(initialType);
tool.showDialog(selectionDialog);
return selectionDialog.getUserChosenDataType();
}
@@ -185,47 +81,4 @@ public class ChooseDataTypeAction extends DockingAction {
}
return false;
}
private int getMaxSizeInStructure(Structure struct, int index) {
int n = struct.getNumComponents();
DataTypeComponent dtc = struct.getComponent(index++);
int length = dtc.getLength();
while (index < n) {
dtc = struct.getComponent(index++);
DataType dataType = dtc.getDataType();
if (dataType != DataType.DEFAULT) {
break;
}
length += dtc.getLength();
}
return length;
}
private int getMaxSize(Program program, Address addr) {
// can't go past the end of a block to start with
Address maxAddr = program.getMemory().getBlock(addr).getEnd();
// get the next non undefined element in memory
Instruction instr = program.getListing().getInstructionAfter(addr);
if (instr != null) {
Address instrAddr = instr.getMinAddress();
if (instrAddr.compareTo(maxAddr) < 0) {
maxAddr = instrAddr.subtract(1);
}
}
Data data = DataUtilities.getNextNonUndefinedDataAfter(program, addr, maxAddr);
if (data != null) {
Address dataAddr = data.getMinAddress();
if (dataAddr.compareTo(maxAddr) < 0) {
maxAddr = dataAddr.subtract(1);
}
}
long length = maxAddr.subtract(addr) + 1;
SystemUtilities.assertTrue(length > 0,
"Subtraction an address from the max address in its block should never be negative");
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
}
}
@@ -238,31 +238,13 @@ public class DataPlugin extends Plugin implements DataService {
dtmService.addDataTypeManagerChangeListener(adapter);
}
boolean isEditDataTypeAllowed(ListingActionContext context) {
Data data = getDataUnit(context);
if (data == null || dtmService == null) {
return false;
}
if (dtmService.isEditable(data.getBaseDataType())) {
return true;
}
Data pdata = data.getParent();
if (pdata != null) {
if (dtmService.isEditable(pdata.getBaseDataType())) {
return true;
}
}
return false;
}
DataType getEditableDataTypeFromContext(ListingActionContext context) {
ProgramSelection currentSelection = context.getSelection();
Program currentProgram = context.getProgram();
DataType editableDataType = null;
ProgramSelection selection = context.getSelection();
Program program = context.getProgram();
Data data = null;
if (currentSelection != null && !currentSelection.isEmpty()) {
Listing listing = currentProgram.getListing();
boolean isDataOnly = !listing.getInstructions(currentSelection, true).hasNext();
if (selection != null && !selection.isEmpty()) {
Listing listing = program.getListing();
boolean isDataOnly = !listing.getInstructions(selection, true).hasNext();
if (isDataOnly) {
data = getDataUnit(context);
}
@@ -270,30 +252,30 @@ public class DataPlugin extends Plugin implements DataService {
else {
data = getDataUnit(context);
}
if (data != null) {
if (dtmService != null) {
DataType baseDt = data.getBaseDataType();
if (dtmService.isEditable(baseDt)) {
editableDataType = baseDt;
}
else {
Data pdata = data.getParent();
if (pdata != null) {
baseDt = pdata.getBaseDataType();
if (dtmService.isEditable(baseDt)) {
editableDataType = baseDt;
}
}
}
}
}
return editableDataType;
return getEditableDataType(data);
}
private DataType getEditableDataType(Data data) {
if (data == null || dtmService == null) {
return null;
}
DataType baseDt = data.getBaseDataType();
if (dtmService.isEditable(baseDt)) {
return baseDt;
}
Data pdata = data.getParent();
if (pdata != null) {
baseDt = pdata.getBaseDataType();
if (dtmService.isEditable(baseDt)) {
return baseDt;
}
}
return null;
}
/**
* @see ghidra.app.services.DataService#createData(ghidra.program.model.data.DataType,
* ghidra.app.context.ListingActionContext, boolean)
*/
@Override
public boolean createData(DataType dt, ListingActionContext context,
boolean enableConflictHandling) {
@@ -307,8 +289,7 @@ public class DataPlugin extends Plugin implements DataService {
}
/*
* This version uses the ProgramActionContext and does not depend on any
* plugin's currentProgram
* This version uses the ListingActionContext and does not depend on any plugin's currentProgram
*/
boolean doCreateData(ListingActionContext context, DataType dt) {
ProgramSelection selection = context.getSelection();
@@ -316,7 +297,7 @@ public class DataPlugin extends Plugin implements DataService {
Program program = context.getProgram();
dt = dt.clone(program.getDataTypeManager());
boolean didCreateData = true;
boolean didCreateData = false;
if (selection != null && !selection.isEmpty()) {
didCreateData = createDataForSelection(program, dt, selection);
}
@@ -375,7 +356,7 @@ public class DataPlugin extends Plugin implements DataService {
Listing listing = program.getListing();
Data data = listing.getDataAt(start);
if (data == null) {
tool.setStatusInfo("Invalid data location");
tool.setStatusInfo("Invalid data location. Cannot create data at " + start + '.');
return false;
}
@@ -393,10 +374,13 @@ public class DataPlugin extends Plugin implements DataService {
end = start.addNoWrap(newSize - 1);
}
catch (AddressOverflowException e) {
tool.setStatusInfo("Invalid data location. Not enough space at " + start + " for " +
newSize + " bytes.");
return false;
}
if (intstrutionExists(listing, dataType, start, end)) {
tool.setStatusInfo("Invalid data location. Instruction exists at " + start + '.');
return false;
}
@@ -415,7 +399,7 @@ public class DataPlugin extends Plugin implements DataService {
Data definedData =
DataUtilities.getNextNonUndefinedDataAfter(program, start, blockMaxAddress);
if (dataExists(program, dataType, definedData, start, end)) {
return false;
return false; // status updated in 'dataExists()' call
}
return true;
@@ -516,58 +500,6 @@ public class DataPlugin extends Plugin implements DataService {
return dataTypeInstance.getLength();
}
boolean doCreateData(Program program, ProgramLocation loc, ProgramSelection sel, DataType dt) {
return doCreateData(program, loc, sel, dt, true);
}
boolean doCreateData(Program program, ProgramLocation loc, ProgramSelection sel, DataType dt,
boolean convertPointers) {
// Handle selection case
boolean rc = true;
if (sel != null && !sel.isEmpty()) {
BackgroundCommand cmd;
Address start = sel.getMinAddress();
InteriorSelection interSel = sel.getInteriorSelection();
if (interSel != null) {
int[] startPath = interSel.getFrom().getComponentPath();
int length = (int) sel.getNumAddresses(); // interior selections can't be that big
cmd = new CreateDataInStructureBackgroundCmd(start, startPath, length, dt,
convertPointers);
}
else {
cmd = new CreateDataBackgroundCmd(sel, dt, convertPointers);
}
if (sel.getNumAddresses() < DataPlugin.BACKGROUND_SELECTION_THRESHOLD) {
rc = tool.execute(cmd, program);
}
else {
getPluginTool().executeBackgroundCommand(cmd, program);
}
}
// Handle single location case
else if (loc != null) {
Address start = loc.getAddress();
int[] startPath = loc.getComponentPath();
Command cmd;
if (startPath != null && startPath.length != 0) {
cmd = new CreateDataInStructureCmd(start, startPath, dt, convertPointers);
}
else {
if (!checkEnoughSpace(program, start, dt, convertPointers)) {
return false;
}
cmd = new CreateDataCmd(start, dt, false, convertPointers);
}
rc = getPluginTool().execute(cmd, program);
}
updateRecentlyUsed(dt);
return rc;
}
PluginTool getPluginTool() {
return tool;
}
@@ -741,9 +673,6 @@ public class DataPlugin extends Plugin implements DataService {
}
}
/**
* @see ghidra.framework.plugintool.Plugin#dispose()
*/
@Override
public void dispose() {
favoritesUpdateManager.dispose();
@@ -756,10 +685,6 @@ public class DataPlugin extends Plugin implements DataService {
createStructureAction.dispose();
}
/**
* @see ghidra.app.plugin.ProgramPlugin#locationChanged(ghidra.program.util.ProgramLocation)
* @see ghidra.app.plugin.ProgramPlugin#selectionChanged(ghidra.program.util.ProgramSelection)
*/
Data getDataUnit(ListingActionContext context) {
ProgramLocation location = context.getLocation();
ProgramSelection selection = context.getSelection();
@@ -69,7 +69,7 @@ public class CreateTypeDefDialog extends DialogComponentProvider {
// data type info
dataTypeEditor =
new DataTypeSelectionEditor(plugin.getTool(), Integer.MAX_VALUE, AllowedDataTypes.ALL);
new DataTypeSelectionEditor(plugin.getTool(), AllowedDataTypes.ALL);
panel.add(new GLabel("Data type:"));
panel.add(dataTypeEditor.getEditorComponent());
@@ -373,7 +373,8 @@ public class FunctionPlugin extends Plugin implements DataService {
/**
* Get an iterator over all functions overlapping the current selection.
* If there is no selection any functions overlapping the current location.
*
*
* @param context the context
* @return Iterator over functions
*/
public Iterator<Function> getFunctions(ListingActionContext context) {
@@ -470,11 +471,10 @@ public class FunctionPlugin extends Plugin implements DataService {
* Lay down the specified dataType on a function return, parameter or local variable
* based upon the programActionContext. Pointer conversion will be handled
* by merging the existing dataType with the specified dataType.
* @param dataType The DataType to create.
* @param dt The DataType to create.
* @param programActionContext action context
* @param promptForConflictRemoval if true and specified dataType results in a storage conflict,
* @param enableConflictHandling if true and specified dataType results in a storage conflict,
* user may be prompted for removal of conflicting variables (not applicable for return type)
* @return True if the DataType could be created at the given location.
*/
@Override
public boolean createData(DataType dt, ListingActionContext programActionContext,
@@ -483,11 +483,12 @@ public class FunctionPlugin extends Plugin implements DataService {
}
/**
* This method is the same as {@link #createData(DataType, ProgramLocation)}, except that this
* method will use the given value of <tt>convertPointers</tt> to determine if the new
* DataType should be made into a pointer if the existing DataType is a pointer.
* @param dataType The DataType to create.
* @param location The location at which to create the DataType.
* This method is the same as {@link #createData(DataType, ListingActionContext, boolean)},
* except that this method will use the given value of <tt>convertPointers</tt> to determine
* if the new DataType should be made into a pointer if the existing DataType is a pointer.
*
* @param dataType the DataType to create
* @param context the context containing the location at which to create the DataType
* @param convertPointers True signals to convert the given DataType to a pointer if there is
* an existing pointer at the specified location.
* @param promptForConflictRemoval if true and specified dataType results in a storage conflict,
@@ -609,23 +610,12 @@ public class FunctionPlugin extends Plugin implements DataService {
return null;
}
// private boolean checkStackVarToFit(Function fun, StackVariable var, DataType dt) {
// if (var.getDataType() instanceof Pointer) {
// return true;
// }
// int startOffset = var.getLength();
// if (startOffset < 0) startOffset = 1;
// int size = getMaxStackVariableSize(fun, var);
// if (size < 0) return true;
// return size >= dt.getLength();
// }
/**
* Return the maximum data type length permitted
* for the specified local variable. A -1 returned
* value indicates no limit imposed.
* @param fun
* @param var
* Return the maximum data type length permitted for the specified local variable. A -1
* returned value indicates no limit imposed.
*
* @param fun the function
* @param var the variable
* @return maximum data type length permitted for var
*/
int getMaxStackVariableSize(Function fun, Variable var) {
@@ -61,7 +61,7 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor implements TableCel
}
private void init() {
editor = new DataTypeSelectionEditor(service, -1, DataTypeParser.AllowedDataTypes.ALL);
editor = new DataTypeSelectionEditor(service, DataTypeParser.AllowedDataTypes.ALL);
editor.setTabCommitsEdit(true);
editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses
@@ -88,25 +88,16 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor implements TableCel
}
};
dataTypeChooserButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
DataType dataType = service.getDataType((String) null);
if (dataType != null) {
editor.setCellEditorValue(dataType);
editor.stopCellEditing();
}
else {
editor.cancelCellEditing();
}
}
});
dataTypeChooserButton.addActionListener(e -> SwingUtilities.invokeLater(() -> {
DataType dataType = service.getDataType((String) null);
if (dataType != null) {
editor.setCellEditorValue(dataType);
editor.stopCellEditing();
}
});
else {
editor.cancelCellEditing();
}
}));
FocusAdapter focusListener = new FocusAdapter() {
@Override
@@ -22,7 +22,6 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.felix.framework.FrameworkFactory;
@@ -63,8 +62,7 @@ public class BundleHost {
private static final String SAVE_STATE_TAG_ACTIVE = "BundleHost_ACTIVE";
private static final String SAVE_STATE_TAG_SYSTEM = "BundleHost_SYSTEM";
Map<ResourceFile, GhidraBundle> fileToBundleMap = new HashMap<>();
Map<String, GhidraBundle> bundleLocationToBundleMap = new HashMap<>();
private final BundleMap bundleMap = new BundleMap();
BundleContext frameworkBundleContext;
Framework felixFramework;
@@ -93,7 +91,7 @@ public class BundleHost {
* @return false if the bundle was already enabled
*/
public boolean enable(ResourceFile bundleFile) {
GhidraBundle bundle = fileToBundleMap.get(bundleFile);
GhidraBundle bundle = bundleMap.get(bundleFile);
if (bundle == null) {
bundle = add(bundleFile, true, false);
return true;
@@ -139,7 +137,7 @@ public class BundleHost {
* @return a {@link GhidraBundle} or {@code null}
*/
public GhidraBundle getExistingGhidraBundle(ResourceFile bundleFile) {
GhidraBundle bundle = fileToBundleMap.get(bundleFile);
GhidraBundle bundle = bundleMap.get(bundleFile);
if (bundle == null) {
Msg.showError(this, null, "ghidra bundle cache",
"getExistingGhidraBundle expected a GhidraBundle created at " + bundleFile +
@@ -156,7 +154,7 @@ public class BundleHost {
* @return a {@link GhidraBundle} or {@code null}
*/
public GhidraBundle getGhidraBundle(ResourceFile bundleFile) {
return fileToBundleMap.get(bundleFile);
return bundleMap.get(bundleFile);
}
private static GhidraBundle createGhidraBundle(BundleHost bundleHost, ResourceFile bundleFile,
@@ -189,25 +187,11 @@ public class BundleHost {
*/
public GhidraBundle add(ResourceFile bundleFile, boolean enabled, boolean systemBundle) {
GhidraBundle bundle = createGhidraBundle(this, bundleFile, enabled, systemBundle);
fileToBundleMap.put(bundleFile, bundle);
bundleLocationToBundleMap.put(bundle.getLocationIdentifier(), bundle);
bundleMap.add(bundle);
fireBundleAdded(bundle);
return bundle;
}
private Set<ResourceFile> dedupeBundleFiles(List<ResourceFile> bundleFiles) {
Set<ResourceFile> dedupedBundleFiles = new HashSet<>(bundleFiles);
Iterator<ResourceFile> bundleFileIterator = dedupedBundleFiles.iterator();
while (bundleFileIterator.hasNext()) {
ResourceFile bundleFile = bundleFileIterator.next();
if (fileToBundleMap.containsKey(bundleFile)) {
bundleFileIterator.remove();
Msg.warn(this, "adding an already managed bundle: " + bundleFile.getAbsolutePath());
}
}
return dedupedBundleFiles;
}
/**
* Create new GhidraBundles and add to the list of managed bundles. All GhidraBundles created
* with the same {@code enabled} and {@code systemBundle} values.
@@ -219,17 +203,8 @@ public class BundleHost {
*/
public Collection<GhidraBundle> add(List<ResourceFile> bundleFiles, boolean enabled,
boolean systemBundle) {
Set<ResourceFile> dedupedBundleFiles = dedupeBundleFiles(bundleFiles);
Map<ResourceFile, GhidraBundle> newBundleMap = dedupedBundleFiles.stream()
.collect(Collectors.toUnmodifiableMap(Function.identity(),
bundleFile -> createGhidraBundle(BundleHost.this, bundleFile, enabled,
systemBundle)));
fileToBundleMap.putAll(newBundleMap);
bundleLocationToBundleMap.putAll(newBundleMap.values()
.stream()
.collect(Collectors.toUnmodifiableMap(GhidraBundle::getLocationIdentifier,
Function.identity())));
Collection<GhidraBundle> newBundles = newBundleMap.values();
Collection<GhidraBundle> newBundles = bundleMap.computeAllIfAbsent(bundleFiles,
bundleFile -> createGhidraBundle(BundleHost.this, bundleFile, enabled, systemBundle));
fireBundlesAdded(newBundles);
return newBundles;
}
@@ -240,10 +215,7 @@ public class BundleHost {
* @param bundles the bundles to add
*/
private void add(List<GhidraBundle> bundles) {
for (GhidraBundle bundle : bundles) {
fileToBundleMap.put(bundle.getFile(), bundle);
bundleLocationToBundleMap.put(bundle.getLocationIdentifier(), bundle);
}
bundleMap.addAll(bundles);
fireBundlesAdded(bundles);
}
@@ -253,8 +225,7 @@ public class BundleHost {
* @param bundleFile the file of the bundle to remove
*/
public void remove(ResourceFile bundleFile) {
GhidraBundle bundle = fileToBundleMap.remove(bundleFile);
bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
GhidraBundle bundle = bundleMap.remove(bundleFile);
fireBundleRemoved(bundle);
}
@@ -264,8 +235,7 @@ public class BundleHost {
* @param bundleLocation the location id of the bundle to remove
*/
public void remove(String bundleLocation) {
GhidraBundle bundle = bundleLocationToBundleMap.remove(bundleLocation);
fileToBundleMap.remove(bundle.getFile());
GhidraBundle bundle = bundleMap.remove(bundleLocation);
fireBundleRemoved(bundle);
}
@@ -275,8 +245,7 @@ public class BundleHost {
* @param bundle the bundle to remove
*/
public void remove(GhidraBundle bundle) {
fileToBundleMap.remove(bundle.getFile());
bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
bundleMap.remove(bundle);
fireBundleRemoved(bundle);
}
@@ -286,10 +255,7 @@ public class BundleHost {
* @param bundles the bundles to remove
*/
public void remove(Collection<GhidraBundle> bundles) {
for (GhidraBundle bundle : bundles) {
fileToBundleMap.remove(bundle.getFile());
bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
}
bundleMap.removeAll(bundles);
fireBundlesRemoved(bundles);
}
@@ -338,7 +304,7 @@ public class BundleHost {
* @return all the bundles
*/
public Collection<GhidraBundle> getGhidraBundles() {
return fileToBundleMap.values();
return bundleMap.getGhidraBundles();
}
/**
@@ -347,7 +313,7 @@ public class BundleHost {
* @return all the bundle files
*/
public Collection<ResourceFile> getBundleFiles() {
return fileToBundleMap.keySet();
return bundleMap.getBundleFiles();
}
void dumpLoadedBundles() {
@@ -861,7 +827,7 @@ public class BundleHost {
boolean isEnabled = bundleIsEnabled[i];
boolean isActive = bundleIsActive[i];
boolean isSystem = bundleIsSystem[i];
GhidraBundle bundle = fileToBundleMap.get(bundleFile);
GhidraBundle bundle = bundleMap.get(bundleFile);
if (bundle != null) {
if (isEnabled != bundle.isEnabled()) {
bundle.setEnabled(isEnabled);
@@ -900,14 +866,15 @@ public class BundleHost {
* @param saveState the state object
*/
public void saveManagedBundleState(SaveState saveState) {
int numBundles = fileToBundleMap.size();
Collection<GhidraBundle> bundles = bundleMap.getGhidraBundles();
int numBundles = bundles.size();
String[] bundleFiles = new String[numBundles];
boolean[] bundleIsEnabled = new boolean[numBundles];
boolean[] bundleIsActive = new boolean[numBundles];
boolean[] bundleIsSystem = new boolean[numBundles];
int index = 0;
for (GhidraBundle bundle : fileToBundleMap.values()) {
for (GhidraBundle bundle : bundles) {
bundleFiles[index] = generic.util.Path.toPathString(bundle.getFile());
bundleIsEnabled[index] = bundle.isEnabled();
bundleIsActive[index] = bundle.isActive();
@@ -1081,7 +1048,7 @@ public class BundleHost {
GhidraBundle bundle;
switch (event.getType()) {
case BundleEvent.STARTED:
bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
bundle = bundleMap.getBundleAtLocation(osgiBundle.getLocation());
if (bundle != null) {
fireBundleActivationChange(bundle, true);
}
@@ -1093,7 +1060,7 @@ public class BundleHost {
break;
// force "inactive" updates for all other states
default:
bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
bundle = bundleMap.getBundleAtLocation(osgiBundle.getLocation());
if (bundle != null) {
fireBundleActivationChange(bundle, false);
}
@@ -0,0 +1,225 @@
/* ###
* 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 ghidra.app.plugin.core.osgi;
import java.util.*;
import java.util.concurrent.locks.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import generic.jar.ResourceFile;
/**
* A thread-safe container that maps {@link GhidraBundle}s by file and bundle location.
*/
public class BundleMap {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private final Map<ResourceFile, GhidraBundle> bundlesByFile = new HashMap<>();
private final Map<String, GhidraBundle> bundlesByLocation = new HashMap<>();
/**
* Maps associations between a bundle, its file, and its bundle location.
*
* @param bundle a GhidraBundle object
*/
public void add(GhidraBundle bundle) {
writeLock.lock();
try {
bundlesByFile.put(bundle.getFile(), bundle);
bundlesByLocation.put(bundle.getLocationIdentifier(), bundle);
}
finally {
lock.writeLock().unlock();
}
}
/**
* Maps bundles in a collection.
*
* <p>This is the same as calling {@link BundleMap#add(GhidraBundle)} for each bundle in {@code bundles}.
*
* @param bundles a collection of GhidraBundle objects
*/
public void addAll(Collection<GhidraBundle> bundles) {
writeLock.lock();
try {
for (GhidraBundle bundle : bundles) {
bundlesByFile.put(bundle.getFile(), bundle);
bundlesByLocation.put(bundle.getLocationIdentifier(), bundle);
}
}
finally {
writeLock.unlock();
}
}
/**
* Removes the mappings of a bundle.
*
* @param bundle a GhidraBundle object
*/
public void remove(GhidraBundle bundle) {
writeLock.lock();
try {
bundlesByFile.remove(bundle.getFile());
bundlesByLocation.remove(bundle.getLocationIdentifier());
}
finally {
writeLock.unlock();
}
}
/**
* Removes all mappings of each bundle from a collection.
*
* This is the same as calling {@link #remove(GhidraBundle)} for each bundle in {@code bundles}.
*
* @param bundles a collection of GhidraBundle objects
*/
public void removeAll(Collection<GhidraBundle> bundles) {
writeLock.lock();
try {
for (GhidraBundle bundle : bundles) {
bundlesByFile.remove(bundle.getFile());
bundlesByLocation.remove(bundle.getLocationIdentifier());
}
}
finally {
writeLock.unlock();
}
}
/**
* Removes the mapping for a bundle with a given bundle location.
*
* @param bundleLocation a bundle location
* @return the bundle removed
*/
public GhidraBundle remove(String bundleLocation) {
writeLock.lock();
try {
GhidraBundle bundle = bundlesByLocation.remove(bundleLocation);
bundlesByFile.remove(bundle.getFile());
return bundle;
}
finally {
writeLock.unlock();
}
}
/**
* Removes the mapping for a bundle with a given file.
* @param bundleFile a bundle file
* @return the bundle removed
*/
public GhidraBundle remove(ResourceFile bundleFile) {
writeLock.lock();
try {
GhidraBundle bundle = bundlesByFile.remove(bundleFile);
bundlesByLocation.remove(bundle.getLocationIdentifier());
return bundle;
}
finally {
writeLock.unlock();
}
}
/**
* Creates and maps bundles from files in a collection that aren't already mapped.
*
* @param bundleFiles a collection of bundle files
* @param ctor a constructor for a GhidraBundle given a bundle file
* @return the newly created GhidraBundle objects
*/
public Collection<GhidraBundle> computeAllIfAbsent(Collection<ResourceFile> bundleFiles,
Function<ResourceFile, GhidraBundle> ctor) {
writeLock.lock();
try {
Set<ResourceFile> newBundleFiles = new HashSet<>(bundleFiles);
newBundleFiles.removeAll(bundlesByFile.keySet());
List<GhidraBundle> newBundles =
newBundleFiles.stream().map(ctor).collect(Collectors.toList());
addAll(newBundles);
return newBundles;
}
finally {
writeLock.unlock();
}
}
/**
* Returns the bundle with the given location.
*
* @param location a bundle location
* @return the bundle found or null
*/
public GhidraBundle getBundleAtLocation(String location) {
readLock.lock();
try {
return bundlesByLocation.get(location);
}
finally {
readLock.unlock();
}
}
/**
* Returns the bundle with the given file.
*
* @param bundleFile a bundle file
* @return the bundle found or null
*/
public GhidraBundle get(ResourceFile bundleFile) {
readLock.lock();
try {
return bundlesByFile.get(bundleFile);
}
finally {
readLock.unlock();
}
}
/**
* @return the currently mapped bundles
*/
public Collection<GhidraBundle> getGhidraBundles() {
readLock.lock();
try {
return new ArrayList<>(bundlesByFile.values());
}
finally {
readLock.unlock();
}
}
/**
* @return the currently mapped bundle files
*/
public Collection<ResourceFile> getBundleFiles() {
readLock.lock();
try {
return new ArrayList<>(bundlesByFile.keySet());
}
finally {
readLock.unlock();
}
}
}
@@ -133,4 +133,19 @@ public abstract class GhidraScriptProvider
protected void writeBody(PrintWriter writer) {
writer.println(getCommentCharacter() + "TODO Add User Code Here");
}
/**
* Fixup a script name for searching in script directories.
*
* <p>This method is part of a poorly specified behavior that is due for future amendment,
* see {@link GhidraScriptUtil#fixupName(String)}.
*
* @param scriptName the name of the script, must end with this provider's extension
* @return a (relative) file path to the corresponding script
*/
@Deprecated
protected String fixupName(String scriptName) {
return scriptName;
}
}
@@ -28,6 +28,7 @@ import generic.jar.ResourceFile;
import ghidra.app.plugin.core.osgi.BundleHost;
import ghidra.app.plugin.core.osgi.OSGiException;
import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin;
import ghidra.app.util.headless.HeadlessAnalyzer;
import ghidra.framework.Application;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
@@ -139,8 +140,7 @@ public class GhidraScriptUtil {
}
catch (IOException e) {
Msg.error(GhidraScriptUtil.class,
"Failed to find script in any script directory: " + sourceFile.toString(),
e);
"Failed to find script in any script directory: " + sourceFile.toString(), e);
}
return null;
}
@@ -293,14 +293,7 @@ public class GhidraScriptUtil {
* @return the Ghidra script provider
*/
public static GhidraScriptProvider getProvider(ResourceFile scriptFile) {
String scriptFileName = scriptFile.getName().toLowerCase();
for (GhidraScriptProvider provider : getProviders()) {
if (scriptFileName.endsWith(provider.getExtension().toLowerCase())) {
return provider;
}
}
return null;
return findProvider(scriptFile.getName());
}
/**
@@ -310,13 +303,23 @@ public class GhidraScriptUtil {
* @return true if a provider exists that can process the specified file
*/
public static boolean hasScriptProvider(ResourceFile scriptFile) {
String scriptFileName = scriptFile.getName().toLowerCase();
return findProvider(scriptFile.getName()) != null;
}
/**
* Find the provider whose extension matches the given filename extension.
*
* @param fileName name of script file
* @return the first matching provider or null if no provider matches
*/
private static GhidraScriptProvider findProvider(String fileName) {
fileName = fileName.toLowerCase();
for (GhidraScriptProvider provider : getProviders()) {
if (scriptFileName.endsWith(provider.getExtension().toLowerCase())) {
return true;
if (fileName.endsWith(provider.getExtension().toLowerCase())) {
return provider;
}
}
return false;
return null;
}
/**
@@ -362,31 +365,35 @@ public class GhidraScriptUtil {
}
/**
* Fixup name issues, such as package parts in the name and inner class names.
* Fix script name issues for searching in script directories.
* If no provider can be identified, Java is assumed.
*
* <p>This method can handle names with or without '.java' at the end; names with
* '$' (inner classes) and names with '.' characters for package separators
* <p>This method is part of a poorly specified behavior that is due for future amendment.
*
* <p>It is used by {@link GhidraScript#runScript(String)} methods,
* {@link #createNewScript(String, String, ResourceFile, List)}, and by {@link HeadlessAnalyzer} for
* {@code preScript} and {@code postScript}. The intent was to allow some freedom in how a user specifies
* a script in two ways: 1) if the extension is omitted ".java" is assumed and 2) if a Java class name is
* given it's converted to a relative path.
*
* @param name the name of the script
* @return the name as a '.java' file path (with '/'s and not '.'s)
* @return the name as a file path
*/
@Deprecated
static String fixupName(String name) {
if (name.endsWith(".java")) {
name = name.substring(0, name.length() - 5);
GhidraScriptProvider provider = findProvider(name);
// assume Java if no provider matched
if (provider == null) {
name = name + ".java";
provider = findProvider(".java");
}
String path = name.replace('.', '/');
int innerClassIndex = path.indexOf('$');
if (innerClassIndex != -1) {
path = path.substring(0, innerClassIndex);
}
return path + ".java";
return provider.fixupName(name);
}
static ResourceFile findScriptFileInPaths(Collection<ResourceFile> scriptDirectories,
String filename) {
String name) {
String validatedName = fixupName(filename);
String validatedName = fixupName(name);
for (ResourceFile resourceFile : scriptDirectories) {
if (resourceFile.isDirectory()) {
@@ -164,4 +164,29 @@ public class JavaScriptProvider extends GhidraScriptProvider {
return "//";
}
/**
*
* Fix script name for search in script directories, such as Java package parts in the name and inner class names.
*
* <p>This method can handle names with '$' (inner classes) and names with '.'
* characters for package separators
*
* <p>It is part of a poorly specified behavior that is due for future amendment,
* see {@link GhidraScriptUtil#fixupName(String)}.
*
* @param scriptName the name of the script
* @return the name as a '.java' file path (with '/'s and not '.'s)
*/
@Override
protected String fixupName(String scriptName) {
scriptName = scriptName.substring(0, scriptName.length() - 5);
String path = scriptName.replace('.', '/');
int innerClassIndex = path.indexOf('$');
if (innerClassIndex != -1) {
path = path.substring(0, innerClassIndex);
}
return path + ".java";
}
}
@@ -20,13 +20,9 @@ import ghidra.framework.plugintool.ServiceInfo;
import ghidra.program.model.data.DataType;
/**
* Data Creation service.
*
* NOTE: This version is dependant on the currentProgram of the implementing
* plugin class. If you want a version that is not dependant on it, please use
* DataCreationService
* Service for creating data
*/
@ServiceInfo(description = "Data creation service.")
@ServiceInfo(description = "Data creation service")
public interface DataService {
/**
@@ -42,13 +38,12 @@ public interface DataService {
/**
* Apply the given data type at a location.
*
* @param dt
* dataType to create at the location
* @param context
* the context containing program, location, and selection information
* @param enableConflictHandling
* if true, the service may prompt the user to resolve data conflicts
* @param dt data type to create at the location
* @param context the context containing program, location, and selection information
* @param enableConflictHandling if true, the service may prompt the user to resolve data
* conflicts
* @return true if the data could be created at the current location
*/
public boolean createData(DataType dt, ListingActionContext context, boolean enableConflictHandling);
public boolean createData(DataType dt, ListingActionContext context,
boolean enableConflictHandling);
}
@@ -63,7 +63,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
private void buildEditor() {
removeWorkPanel();
editor = new DataTypeSelectionEditor(pluginTool, maxSize, allowedTypes);
editor = new DataTypeSelectionEditor(pluginTool, allowedTypes);
editor.setPreferredDataTypeManager(dtm);
editor.setConsumeEnterKeyPress(false); // we want to handle Enter key presses
editor.addCellEditorListener(new CellEditorListener() {
@@ -171,6 +171,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider {
* This method is useful for widgets that have embedded editors that launch this dialog. For
* these editors, like tables, it is nice to be able to tab through various editors. This
* method allows these editors to keep this functionality, even though a new dialog was shown.
* @param doesCommit true commits edits on Tab press
*/
public void setTabCommitsEdit(boolean doesCommit) {
editor.setTabCommitsEdit(doesCommit);
@@ -59,7 +59,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
private DropDownSelectionTextField<DataType> selectionField;
private JButton browseButton;
private DataTypeManagerService dataTypeManagerService;
private int maxSize = -1;
private DataTypeManager dataTypeManager;
private DataTypeParser.AllowedDataTypes allowedDataTypes;
@@ -69,12 +68,12 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// optional path to initially select in the data type chooser tree
private TreePath initiallySelectedTreePath;
public DataTypeSelectionEditor(ServiceProvider serviceProvider, int maxSize,
public DataTypeSelectionEditor(ServiceProvider serviceProvider,
DataTypeParser.AllowedDataTypes allowedDataTypes) {
this(serviceProvider.getService(DataTypeManagerService.class), maxSize, allowedDataTypes);
this(serviceProvider.getService(DataTypeManagerService.class), allowedDataTypes);
}
public DataTypeSelectionEditor(DataTypeManagerService service, int maxSize,
public DataTypeSelectionEditor(DataTypeManagerService service,
DataTypeParser.AllowedDataTypes allowedDataTypes) {
if (service == null) {
@@ -82,7 +81,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
}
this.dataTypeManagerService = service;
this.maxSize = maxSize;
this.allowedDataTypes = allowedDataTypes;
init();
@@ -102,7 +100,10 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
}
/**
* Sets whether this editor should consumer Enter key presses
* @see DropDownSelectionTextField#setConsumeEnterKeyPress(boolean)
*
* @param consume true to consume
*/
public void setConsumeEnterKeyPress(boolean consume) {
selectionField.setConsumeEnterKeyPress(consume);
@@ -276,6 +277,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
/**
* Returns the direction of the user triggered navigation; null if the user did not trigger
* navigation out of this component.
* @return the direction
*/
public NavigationDirection getNavigationDirection() {
return navigationDirection;
@@ -362,10 +364,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
catch (CancelledException e) {
return false;
}
if (newDataType != null) {
if (maxSize >= 0 && newDataType.getLength() > newDataType.getLength()) {
throw new InvalidDataTypeException("data-type larger than " + maxSize + " bytes");
}
selectionField.setSelectedValue(newDataType);
return true;
}
@@ -95,8 +95,8 @@ public class DataTypeParser {
* the source data type manager, this means that all data type managers will be used when
* resolving data types.
*
* @param dataTypeManagerService
* @param allowedTypes
* @param dataTypeManagerService data-type manager tool service, or null
* @param allowedTypes constrains which data-types may be parsed
*/
public DataTypeParser(DataTypeQueryService dataTypeManagerService,
AllowedDataTypes allowedTypes) {
@@ -17,8 +17,7 @@ package ghidra.app.merge.datatypes;
import static org.junit.Assert.*;
import org.junit.Assert;
import org.junit.Test;
import org.junit.*;
import ghidra.program.database.*;
import ghidra.program.model.data.*;
@@ -1247,6 +1246,122 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
}
@Test
@Ignore
public void testEditStructureWithReplacementAndRemoval() throws Exception {
// See GP-585 for design issue preventing this test from passing
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyPrivate(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
Structure s = (Structure) dtm.getDataType("/Category5/Test");
DataType dt = dtm.getDataType("/MISC/FooTypedef");
s.setFlexibleArrayComponent(dt, "foo", "");
commit = true;
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
TypeDef td = (TypeDef) dtm.getDataType("/BF");
//
// NOTE: Merge does not handle datatype replacements as one might hope
// If latest version has defined data/components based upon a type which has
// been replaced in private, the replaced datatype will be treated as removed
//
dtm.replaceDataType(td, new TypedefDataType("NewBF", IntegerDataType.dataType),
true);
DataType dt = dtm.getDataType("/MISC/FooTypedef");
dtm.remove(dt, TaskMonitor.DUMMY);
commit = true;
}
finally {
program.endTransaction(transactionID, commit);
}
DataType dt1 = dtm.getDataType("/BF");
assertNull(dt1);
}
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
TypeDef td = new TypedefDataType("BF", IntegerDataType.dataType);
Structure struct = new StructureDataType(new CategoryPath("/Category5"), "Test",
0, program.getDataTypeManager());
struct.add(td);
struct.insertBitFieldAt(3, 2, 6, td, 2, "bf1", null);
struct.insertBitFieldAt(3, 2, 4, td, 2, "bf2", null);
struct.add(new WordDataType());
struct.add(new QWordDataType());
struct.setFlexibleArrayComponent(td, "flex", "my flex");
dtm.addDataType(struct, null);
commit = true;
}
catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
}
finally {
program.endTransaction(transactionID, commit);
}
}
});
executeMerge(DataTypeMergeManager.OPTION_MY);
DataTypeManager dtm = resultProgram.getDataTypeManager();
DataType dt = dtm.getDataType("/Category5/Test");
assertTrue(dt instanceof Structure);
Structure s = (Structure) dt;
/** Current Result for /Category5/Test
*
Unaligned
Structure Test {
4 int:2(6) 1 bf1 ""
4 int:2(4) 1 bf2 ""
5 word 2 null ""
7 qword 8 null ""
}
Size = 15 Actual Alignment = 1
*
* See assertion below for preferred result
*/
//@formatter:off
assertEquals("/Category5/Test\n" +
"Unaligned\n" +
"Structure Test {\n" +
" 0 NewBF 4 null \"\"\n" +
" 4 NewBF:2(6) 1 bf1 \"\"\n" +
" 4 NewBF:2(4) 1 bf2 \"\"\n" +
" 5 word 2 null \"\"\n" +
" 7 qword 8 null \"\"\n" +
" Undefined1[0] 0 foo \"\"\n" + // reflects removal of /MISC/FooTypedef
"}\n" +
"Size = 15 Actual Alignment = 1\n", s.toString());
//@formatter:on
}
@Test
public void testEditUnions() throws Exception {
@@ -46,7 +46,8 @@ import generic.test.AbstractGTest;
import generic.util.WindowUtilities;
import generic.util.image.ImageUtils;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.archive.*;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
@@ -1175,7 +1176,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
JPanel editorPanel = new JPanel(new BorderLayout());
DataTypeSelectionEditor editor =
new DataTypeSelectionEditor(tool, -1, AllowedDataTypes.ALL);
new DataTypeSelectionEditor(tool, AllowedDataTypes.ALL);
editor.setPreferredDataTypeManager(program.getDataTypeManager());
editorPanel.add(panelUpdateField, BorderLayout.SOUTH);
@@ -45,14 +45,6 @@ public class CreateDataCmdTest extends AbstractGenericTest {
private Listing listing;
private ProgramBuilder builder;
/**
* Constructor for CreateDataCmdTest.
* @param arg0
*/
public CreateDataCmdTest() {
super();
}
@Before
public void setUp() throws Exception {
program = buildProgram();
@@ -637,8 +629,9 @@ public class CreateDataCmdTest extends AbstractGenericTest {
cmd.applyTo(program);
// Add external reference from pointer
program.getReferenceManager().addExternalReference(addr, "OtherFile", "ExtLabel", null,
SourceType.USER_DEFINED, 0, RefType.DATA);
program.getReferenceManager()
.addExternalReference(addr, "OtherFile", "ExtLabel", null,
SourceType.USER_DEFINED, 0, RefType.DATA);
// Undefined* becomes Byte*
cmd = new CreateDataCmd(addr, false, true, new ByteDataType());
@@ -724,7 +717,8 @@ public class CreateDataCmdTest extends AbstractGenericTest {
assertEquals(10, dt.getLength());
// Byte[] becomes Byte
CreateDataCmd cmd = new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA);
CreateDataCmd cmd =
new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA);
cmd.applyTo(program);
d = listing.getDataAt(addr);
@@ -792,7 +786,8 @@ public class CreateDataCmdTest extends AbstractGenericTest {
assertEquals(10, dt.getLength());
// struct becomes Byte
CreateDataCmd cmd = new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA);
CreateDataCmd cmd =
new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA);
cmd.applyTo(program);
d = listing.getDataAt(addr);
@@ -49,7 +49,7 @@ YACC=bison
# libraries
#INCLUDES=-I$(BFDHOME)/include
INCLUDES=
BFDLIB=-lbfd -liberty -lz
BFDLIB=-lbfd -lz
LNK=
@@ -1066,7 +1066,7 @@ void Architecture::parsePreferSplit(const Element *el)
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) {
splitrecords.push_back(PreferSplitRecord());
splitrecords.emplace_back();
PreferSplitRecord &record( splitrecords.back() );
record.storage.restoreXml( *iter, this );
record.splitoffset = record.storage.size/2;
@@ -1301,6 +1301,7 @@ void Architecture::resetDefaultsInternal(void)
flowoptions = FlowInfo::error_toomanyinstructions;
max_instructions = 100000;
infer_pointers = true;
analyze_for_loops = true;
readonlypropagate = false;
alias_block_level = 2; // Block structs and arrays by default
}
@@ -128,6 +128,7 @@ public:
bool aggressive_ext_trim; ///< Aggressively trim inputs that look like they are sign extended
bool readonlypropagate; ///< true if readonly values should be treated as constants
bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses
bool analyze_for_loops; ///< True if we should attempt conversion of \e whiledo loops to \e for loops
vector<AddrSpace *> inferPtrSpaces; ///< Set of address spaces in which a pointer constant is inferable
int4 funcptr_align; ///< How many bits of alignment a function ptr has
uint4 flowoptions; ///< options passed to flow following engine
@@ -73,11 +73,11 @@ void FlowBlock::addInEdge(FlowBlock *b,uint4 lab)
void FlowBlock::restoreNextInEdge(const Element *el,BlockMap &resolver)
{
intothis.push_back(BlockEdge());
intothis.emplace_back();
BlockEdge &inedge(intothis.back());
inedge.restoreXml(el,resolver);
while(inedge.point->outofthis.size() <= inedge.reverse_index)
inedge.point->outofthis.push_back(BlockEdge());
inedge.point->outofthis.emplace_back();
BlockEdge &outedge(inedge.point->outofthis[inedge.reverse_index]);
outedge.label = 0;
outedge.point = this;
@@ -1254,7 +1254,16 @@ FlowBlock *BlockGraph::nextFlowAfter(const FlowBlock *bl) const
return nextbl;
}
void BlockGraph::finalizePrinting(const Funcdata &data) const
void BlockGraph::finalTransform(Funcdata &data)
{
// Recurse into all the substructures
vector<FlowBlock *>::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter)
(*iter)->finalTransform(data);
}
void BlockGraph::finalizePrinting(Funcdata &data) const
{
// Recurse into all the substructures
@@ -2916,6 +2925,170 @@ void BlockIf::saveXmlBody(ostream &s) const
}
}
/// Try to find a Varnode that represents the controlling \e loop \e variable for \b this loop.
/// The Varnode must be:
/// - tested by the exit condition
/// - have a MULTIEQUAL in the head block
/// - have a modification coming in from the tail block
/// - the modification must be the last op or moveable to the last op
///
/// If the loop variable is found, this routine sets the \e iterateOp and the \e loopDef.
/// \param cbranch is the CBRANCH implementing the loop exit
/// \param head is the head basic-block of the loop
/// \param tail is the tail basic-block of the loop
/// \param lastOp is the precomputed last PcodeOp of tail that isn't a BRANCH
void BlockWhileDo::findLoopVariable(PcodeOp *cbranch,BlockBasic *head,BlockBasic *tail,PcodeOp *lastOp)
{
Varnode *vn = cbranch->getIn(1);
if (!vn->isWritten()) return; // No loop variable found
PcodeOp *op = vn->getDef();
int4 slot = tail->getOutRevIndex(0);
PcodeOpNode path[4];
int4 count = 0;
if (op->isCall() || op->isMarker()) {
return;
}
path[0].op = op;
path[0].slot = 0;
while(count>=0) {
PcodeOp *curOp = path[count].op;
int4 ind = path[count].slot++;
if (ind >= curOp->numInput()) {
count -= 1;
continue;
}
Varnode *nextVn = curOp->getIn(ind);
if (!nextVn->isWritten()) continue;
PcodeOp *defOp = nextVn->getDef();
if (defOp->code() == CPUI_MULTIEQUAL) {
if (defOp->getParent() != head) continue;
Varnode *itvn = defOp->getIn(slot);
if (!itvn->isWritten()) continue;
PcodeOp *possibleIterate = itvn->getDef();
if (possibleIterate->getParent() == tail) { // Found proper head/tail configuration
if (possibleIterate->isMarker())
continue; // No iteration in tail
if (!possibleIterate->isMoveable(lastOp))
continue; // Not the final statement
loopDef = defOp;
iterateOp = possibleIterate;
return; // Found the loop variable
}
}
else {
if (count == 3) continue;
if (defOp->isCall() || defOp->isMarker()) continue;
count += 1;
path[count].op = defOp;
path[count].slot = 0;
}
}
return; // No loop variable found
}
/// Given a control flow loop, try to find a putative initializer PcodeOp for the loop variable.
/// The initializer must be read by read by \e loopDef and by in a block that
/// flows only into the loop. If an initializer is found, then
/// \e initializeOp is set and the lastOp (not including a branch) in the initializer
/// block is returned. Otherwise null is returned.
/// \param head is the head block of the loop
/// \param slot is the block input coming from the loop tail
/// \return the last PcodeOp in the initializer's block
PcodeOp *BlockWhileDo::findInitializer(BlockBasic *head,int4 slot) const
{
if (head->sizeIn() != 2) return (PcodeOp *)0;
slot = 1 - slot;
Varnode *initVn = loopDef->getIn(slot);
if (!initVn->isWritten()) return (PcodeOp *)0;
PcodeOp *res = initVn->getDef();
if (res->isMarker()) return (PcodeOp *)0;
FlowBlock *initialBlock = res->getParent();
if (initialBlock != head->getIn(slot))
return (PcodeOp *)0; // Statement must terminate in block flowing to head
PcodeOp *lastOp = initialBlock->lastOp();
if (lastOp == (PcodeOp *)0) return (PcodeOp *)0;
if (initialBlock->sizeOut() != 1) return (PcodeOp *)0; // Initializer block must flow only to for loop
if (lastOp->isBranch()) {
lastOp = lastOp->previousOp();
if (lastOp == (PcodeOp *)0) return (PcodeOp *)0;
}
initializeOp = res;
return lastOp;
}
/// For-loop initializer or iterator statements must be the final statement in
/// their respective basic block. This method tests that iterateOp/initializeOp (specified
/// by \e slot) is the root of or can be turned into the root of a terminal statement.
/// The root output must be an explicit variable being read by the
/// \e loopDef MULTIEQUAL at the top of the loop. If the root is not the last
/// PcodeOp in the block, an attempt is made to move it.
/// Return the root PcodeOp if all these conditions are met, otherwise return null.
/// \param data is the function containing the while loop
/// \param slot is the slot read by \e loopDef from the output of the statement
/// \return an explicit statement or null
PcodeOp *BlockWhileDo::testTerminal(Funcdata &data,int4 slot) const
{
Varnode *vn = loopDef->getIn(slot);
if (!vn->isWritten()) return (PcodeOp *)0;
PcodeOp *finalOp = vn->getDef();
BlockBasic *parentBlock = (BlockBasic *)loopDef->getParent()->getIn(slot);
PcodeOp *resOp = finalOp;
if (finalOp->code() == CPUI_COPY && finalOp->notPrinted()) {
vn = finalOp->getIn(0);
if (!vn->isWritten()) return (PcodeOp *)0;
resOp = vn->getDef();
if (resOp->getParent() != parentBlock) return (PcodeOp *)0;
}
if (!vn->isExplicit()) return (PcodeOp *)0;
if (resOp->notPrinted())
return (PcodeOp *)0; // Statement MUST be printed
// finalOp MUST be the last op in the basic block (except for the branch)
PcodeOp *lastOp = finalOp->getParent()->lastOp();
if (lastOp->isBranch())
lastOp = lastOp->previousOp();
if (!data.moveRespectingCover(finalOp, lastOp))
return (PcodeOp *)0;
return resOp;
}
/// Make sure the loop variable is involved as input in the iterator statement.
/// \return \b true if the loop variable is an input to the iterator statement
bool BlockWhileDo::testIterateForm(void) const
{
Varnode *targetVn = loopDef->getOut();
HighVariable *high = targetVn->getHigh();
vector<PcodeOpNode> path;
PcodeOp *op = iterateOp;
path.push_back(PcodeOpNode(op,0));
while(!path.empty()) {
PcodeOpNode &node(path.back());
if (node.op->numInput() <= node.slot) {
path.pop_back();
continue;
}
Varnode *vn = node.op->getIn(node.slot);
node.slot += 1;
if (vn->isAnnotation()) continue;
if (vn->getHigh() == high) {
return true;
}
if (vn->isExplicit()) continue; // Truncate at explicit
if (!vn->isWritten()) continue;
op = vn->getDef();
path.push_back(PcodeOpNode(vn->getDef(),0));
}
return false;
}
void BlockWhileDo::markLabelBumpUp(bool bump)
{
@@ -2953,6 +3126,79 @@ FlowBlock *BlockWhileDo::nextFlowAfter(const FlowBlock *bl) const
return nextbl;
}
/// Determine if \b this block can be printed as a \e for loop, with an \e initializer statement
/// extracted from the previous block, and an \e iterator statement extracted from the body.
/// \param data is the function containing \b this loop
void BlockWhileDo::finalTransform(Funcdata &data)
{
BlockGraph::finalTransform(data);
if (!data.getArch()->analyze_for_loops) return;
if (hasOverflowSyntax()) return;
FlowBlock *copyBl = getFrontLeaf();
if (copyBl == (FlowBlock *)0) return;
BlockBasic *head = (BlockBasic *)copyBl->subBlock(0);
if (head->getType() != t_basic) return;
PcodeOp *lastOp = getBlock(1)->lastOp(); // There must be a last op in body, for there to be an iterator statement
if (lastOp == (PcodeOp *)0) return;
BlockBasic *tail = lastOp->getParent();
if (tail->sizeOut() != 1) return;
if (tail->getOut(0) != head) return;
PcodeOp *cbranch = getBlock(0)->lastOp();
if (cbranch == (PcodeOp *)0 || cbranch->code() != CPUI_CBRANCH) return;
if (lastOp->isBranch()) { // Convert lastOp to -point- iterateOp must appear after
lastOp = lastOp->previousOp();
if (lastOp == (PcodeOp *)0) return;
}
findLoopVariable(cbranch, head, tail, lastOp);
if (iterateOp == (PcodeOp *)0) return;
if (iterateOp != lastOp) {
data.opUninsert(iterateOp);
data.opInsertAfter(iterateOp, lastOp);
}
// Try to set up initializer statement
lastOp = findInitializer(head, tail->getOutRevIndex(0));
if (lastOp == (PcodeOp *)0) return;
if (!initializeOp->isMoveable(lastOp)) {
initializeOp = (PcodeOp *)0; // Turn it off
return;
}
if (initializeOp != lastOp) {
data.opUninsert(initializeOp);
data.opInsertAfter(initializeOp, lastOp);
}
}
/// Assume that finalTransform() has run and that all HighVariable merging has occurred.
/// Do any final tests checking that the initialization and iteration statements are good.
/// Extract initialization and iteration statements from their basic blocks.
/// \param data is the function containing the loop
void BlockWhileDo::finalizePrinting(Funcdata &data) const
{
BlockGraph::finalizePrinting(data); // Continue recursing
if (iterateOp == (PcodeOp *)0) return; // For-loop printing not enabled
// TODO: We can check that iterate statement is not too complex
int4 slot = iterateOp->getParent()->getOutRevIndex(0);
iterateOp = testTerminal(data,slot); // Make sure iterator statement is explicit
if (iterateOp == (PcodeOp *)0) return;
if (!testIterateForm()) {
iterateOp = (PcodeOp *)0;
return;
}
if (initializeOp == (PcodeOp *)0)
findInitializer(loopDef->getParent(), slot); // Last chance initializer
if (initializeOp != (PcodeOp *)0)
initializeOp = testTerminal(data,1-slot); // Make sure initializer statement is explicit
data.opMarkNonPrinting(iterateOp);
if (initializeOp != (PcodeOp *)0)
data.opMarkNonPrinting(initializeOp);
}
void BlockDoWhile::markLabelBumpUp(bool bump)
{
@@ -3025,7 +3271,7 @@ BlockSwitch::BlockSwitch(FlowBlock *ind)
void BlockSwitch::addCase(FlowBlock *switchbl,FlowBlock *bl,uint4 gt)
{
caseblocks.push_back(CaseOrder());
caseblocks.emplace_back();
CaseOrder &curcase( caseblocks.back() );
const FlowBlock *basicbl = bl->getFrontLeaf()->subBlock(0);
curcase.block = bl;
@@ -3083,7 +3329,7 @@ void BlockSwitch::grabCaseBasic(FlowBlock *switchbl,const vector<FlowBlock *> &c
}
}
void BlockSwitch::finalizePrinting(const Funcdata &data) const
void BlockSwitch::finalizePrinting(Funcdata &data) const
{
BlockGraph::finalizePrinting(data); // Make sure to still recurse
@@ -170,7 +170,8 @@ public:
virtual void flipInPlaceExecute(void);
virtual bool isComplex(void) const { return true; } ///< Is \b this too complex to be a condition (BlockCondition)
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
virtual void finalizePrinting(const Funcdata &data) const {} ///< Make any final configurations necessary to print the block
virtual void finalTransform(Funcdata &data) {} ///< Do any structure driven final transforms
virtual void finalizePrinting(Funcdata &data) const {} ///< Make any final configurations necessary to print the block
virtual void saveXmlHeader(ostream &s) const; ///< Save basic information as XML attributes
virtual void restoreXmlHeader(const Element *el); ///< Restore basic information for XML attributes
virtual void saveXmlBody(ostream &s) const {} ///< Save detail about components to an XML stream
@@ -296,7 +297,8 @@ public:
virtual void printRaw(ostream &s) const;
virtual void emit(PrintLanguage *lng) const { lng->emitBlockGraph(this); }
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
virtual void finalizePrinting(const Funcdata &data) const;
virtual void finalTransform(Funcdata &data);
virtual void finalizePrinting(Funcdata &data) const;
virtual void saveXmlBody(ostream &s) const;
virtual void restoreXmlBody(List::const_iterator &iter,List::const_iterator enditer,BlockMap &resolver);
void restoreXml(const Element *el,const AddrSpaceManager *m); ///< Restore \b this BlockGraph from an XML stream
@@ -580,8 +582,23 @@ public:
/// Overflow syntax refers to the situation where there is a proper BlockWhileDo structure but
/// the conditional block is too long or complicated to emit as a single conditional expression.
/// An alternate `while(true) { }` form is used instead.
///
/// If an iterator op is provided, the block will be printed using \e for loop syntax,
/// `for(i=0;i<10;++i)` where an \e initializer statement and \e iterator statement are
/// printed alongside the \e condition statement. Otherwise, \e while loop syntax is used
/// `while(i<10)`
class BlockWhileDo : public BlockGraph {
mutable PcodeOp *initializeOp; ///< Statement used as \e for loop initializer
mutable PcodeOp *iterateOp; ///< Statement used as \e for loop iterator
mutable PcodeOp *loopDef; ///< MULTIEQUAL merging loop variable
void findLoopVariable(PcodeOp *cbranch,BlockBasic *head,BlockBasic *tail,PcodeOp *lastOp); ///< Find a \e loop \e variable
PcodeOp *findInitializer(BlockBasic *head,int4 slot) const; ///< Find the for-loop initializer op
PcodeOp *testTerminal(Funcdata &data,int4 slot) const; ///< Test that given statement is terminal and explicit
bool testIterateForm(void) const; ///< Return \b false if the iterate statement is of an unacceptable form
public:
BlockWhileDo(void) { initializeOp = (PcodeOp *)0; iterateOp = (PcodeOp *)0; loopDef = (PcodeOp *)0; } ///< Constructor
PcodeOp *getInitializeOp(void) const { return initializeOp; }
PcodeOp *getIterateOp(void) const { return iterateOp; }
bool hasOverflowSyntax(void) const { return ((getFlags() & f_whiledo_overflow)!=0); } ///< Does \b this require overflow syntax
void setOverflowSyntax(void) { setFlag(f_whiledo_overflow); } ///< Set that \b this requires overflow syntax
virtual block_type getType(void) const { return t_whiledo; }
@@ -590,6 +607,8 @@ public:
virtual void printHeader(ostream &s) const;
virtual void emit(PrintLanguage *lng) const { lng->emitBlockWhileDo(this); }
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
virtual void finalTransform(Funcdata &data);
virtual void finalizePrinting(Funcdata &data) const;
};
/// \brief A loop structure where the condition is checked at the bottom.
@@ -674,7 +693,7 @@ public:
virtual void printHeader(ostream &s) const;
virtual void emit(PrintLanguage *lng) const { lng->emitBlockSwitch(this); }
virtual FlowBlock *nextFlowAfter(const FlowBlock *bl) const;
virtual void finalizePrinting(const Funcdata &data) const;
virtual void finalizePrinting(Funcdata &data) const;
};
/// \brief Helper class for resolving cross-references while deserializing BlockGraph objects
@@ -729,7 +729,7 @@ TraceDAG::BlockTrace *TraceDAG::selectBadEdge(void)
if ((*aiter)->isTerminal()) continue;
if (((*aiter)->top->top == (FlowBlock *)0)&&((*aiter)->bottom==(FlowBlock *)0))
continue; // Never remove virtual edges
badedgelist.push_back(BadEdgeScore());
badedgelist.emplace_back();
BadEdgeScore &score( badedgelist.back() );
score.trace = *aiter;
score.exitproto = score.trace->destnode;
@@ -1125,7 +1125,7 @@ void CollapseStructure::labelLoops(vector<LoopBody *> &looporder)
for(int4 j=0;j<sizein;++j) {
if (bl->isBackEdgeIn(j)) { // back-edge coming in must be from the bottom of a loop
FlowBlock *loopbottom = bl->getIn(j);
loopbody.push_back(LoopBody(bl));
loopbody.emplace_back(bl);
LoopBody &curbody( loopbody.back() );
curbody.addTail(loopbottom);
looporder.push_back( & curbody );
@@ -2095,6 +2095,13 @@ void ConditionalJoin::clear(void)
mergeneed.clear();
}
int4 ActionStructureTransform::apply(Funcdata &data)
{
data.getStructure().finalTransform(data);
return 0;
}
int4 ActionNormalizeBranches::apply(Funcdata &data)
{
@@ -262,6 +262,19 @@ public:
void clear(void); ///< Clear for a new test
};
/// \brief Give each control-flow structure an opportunity to make a final transform
///
/// This is currently used to set up \e for loops via BlockWhileDo
class ActionStructureTransform : public Action {
public:
ActionStructureTransform(const string &g) : Action(0,"structuretransform",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Action *)0;
return new ActionStructureTransform(getGroup());
}
virtual int4 apply(Funcdata &data);
};
/// \brief Flip conditional control-flow so that \e preferred comparison operators are used
///
/// This is used as an alternative to the standard algorithm that structures control-flow, when
@@ -193,7 +193,7 @@ void CallGraph::clearMarks(void)
CallGraphEdge &CallGraph::insertBlankEdge(CallGraphNode *node,int4 slot)
{
node->outedge.push_back(CallGraphEdge());
node->outedge.emplace_back();
if (node->outedge.size() > 1) {
for(int4 i=node->outedge.size()-2;i>=slot;--i) {
int4 newi = i+1;
@@ -255,7 +255,7 @@ void CallGraph::addEdge(CallGraphNode *from,CallGraphNode *to,const Address &add
CallGraphEdge &fromedge( insertBlankEdge(from,i) );
int4 toi = to->inedge.size();
to->inedge.push_back(CallGraphEdge());
to->inedge.emplace_back();
CallGraphEdge &toedge( to->inedge.back() );
fromedge.from = from;
@@ -282,7 +282,7 @@ Address CodeDataAnalysis::disassembleBlock(const Address &addr,const Address &en
}
for(;;) {
disengine.disassemble(curaddr,disresult);
codevec.push_back(CodeUnit());
codevec.emplace_back();
if (!disresult.success) {
codevec.back().flags = CodeUnit::notcode;
codevec.back().size = 1;
@@ -428,16 +428,16 @@ void CodeDataAnalysis::markCrossHits(void)
void CodeDataAnalysis::addTargetHit(const Address &codeaddr,uintb targethit)
{
targethits.push_back(TargetHit());
targethits.back().funcstart = findFunctionStart( codeaddr );
targethits.back().codeaddr = codeaddr;
targethits.back().thunkaddr = Address(glb->translate->getDefaultCodeSpace(),targethit);
Address funcstart = findFunctionStart( codeaddr );
Address thunkaddr = Address(glb->translate->getDefaultCodeSpace(),targethit);
uint4 mask;
map<Address,TargetFeature>::const_iterator titer;
titer = targets.find( targethits.back().thunkaddr );
titer = targets.find( thunkaddr );
if (titer != targets.end())
targethits.back().mask = (*titer).second.featuremask;
mask = (*titer).second.featuremask;
else
throw LowlevelError("Found thunk without a feature mask");
targethits.emplace_back(funcstart,codeaddr,thunkaddr,mask);
}
void CodeDataAnalysis::resolveThunkHit(const Address &codeaddr,uintb targethit)
@@ -524,7 +524,7 @@ bool CodeDataAnalysis::repairJump(const Address &addr,int4 max)
if (curaddr == (*iter).first) break; // Back on cut
disengine.disassemble(curaddr,disresult);
if (!disresult.success) return false;
codevec.push_back(CodeUnit());
codevec.emplace_back();
if ((disresult.flags & CodeUnit::jump)!=0) {
fromto_vec[ AddrLink(curaddr,disresult.jumpaddress) ] = disresult.flags;
}
@@ -77,6 +77,8 @@ public:
Address codeaddr; // Address of instruction refering to target call
Address thunkaddr; // The target call
uint4 mask; // Mask associated with this target
TargetHit(const Address &func,const Address &code,const Address &thunk,uint4 m) :
funcstart(func), codeaddr(code), thunkaddr(thunk) { mask = m; }
bool operator<(const TargetHit &op2) const { return (funcstart < op2.funcstart); }
};
@@ -119,7 +119,7 @@ uintm ParserContext::getContextBits(int4 startbit,int4 size) const
void ParserContext::addCommit(TripleSymbol *sym,int4 num,uintm mask,bool flow,ConstructState *point)
{
contextcommit.push_back(ContextSet());
contextcommit.emplace_back();
ContextSet &set(contextcommit.back());
set.sym = sym;
@@ -4532,7 +4532,7 @@ void ActionInferTypes::propagateOneType(TypeFactory *typegrp,Varnode *vn)
PropagationState *ptr;
vector<PropagationState> state;
state.push_back(PropagationState(vn));
state.emplace_back(vn);
vn->setMark();
while(!state.empty()) {
@@ -4545,7 +4545,7 @@ void ActionInferTypes::propagateOneType(TypeFactory *typegrp,Varnode *vn)
if (propagateTypeEdge(typegrp,ptr->op,ptr->inslot,ptr->slot)) {
vn = (ptr->slot==-1) ? ptr->op->getOut() : ptr->op->getIn(ptr->slot);
ptr->step(); // Make sure to step before push_back
state.push_back(PropagationState(vn));
state.emplace_back(vn);
vn->setMark();
}
else
@@ -5100,6 +5100,7 @@ void ActionDatabase::universalAction(Architecture *conf)
act->addAction( actcleanup );
act->addAction( new ActionPreferComplement("blockrecovery") );
act->addAction( new ActionStructureTransform("blockrecovery") );
act->addAction( new ActionNormalizeBranches("normalizebranches") );
act->addAction( new ActionAssignHigh("merge") );
act->addAction( new ActionMergeRequired("merge") );
@@ -193,7 +193,7 @@ CPoolRecord *ConstantPoolInternal::createRecord(const vector<uintb> &refs)
{
CheapSorter sorter(refs);
pair<map<CheapSorter,CPoolRecord>::iterator,bool> res;
res = cpoolMap.insert(pair<CheapSorter,CPoolRecord>(sorter,CPoolRecord()));
res = cpoolMap.emplace(piecewise_construct,forward_as_tuple(sorter),forward_as_tuple());
if (res.second == false)
throw LowlevelError("Creating duplicate entry in constant pool: "+(*res.first).second.getToken());
return &(*res.first).second;
@@ -53,12 +53,11 @@ SymbolEntry::SymbolEntry(Symbol *sym,uint4 exfl,uint8 h,int4 off,int4 sz,const R
uselimit = rnglist;
}
/// Assuming the boundary offsets have been specified with
/// the constructor, fill in the rest of the data.
/// Establish the boundary offsets and fill in additional data
/// \param data contains the raw initialization data
/// \param a is the starting offset of the entry
/// \param b is the ending offset of the entry
void SymbolEntry::initialize(const EntryInitData &data,uintb a,uintb b)
SymbolEntry::SymbolEntry(const EntryInitData &data,uintb a,uintb b)
{
addr = Address(data.space,a);
@@ -112,7 +112,7 @@ public:
typedef EntrySubsort subsorttype; ///< The sub-sort object for a rangemap
typedef EntryInitData inittype; ///< Initialization data for a SymbolEntry in a rangemap
SymbolEntry(void) {} ///< Constructor for use with rangemap
SymbolEntry(const EntryInitData &data,uintb a,uintb b); ///< Fully initialize \b this
SymbolEntry(Symbol *sym,uint4 exfl,uint8 h,int4 off,int4 sz,const RangeList &rnglist); ///< Construct a dynamic SymbolEntry
bool isPiece(void) const { return ((extraflags&(Varnode::precislo|Varnode::precishi))!=0); } ///< Is \b this a high or low piece of the whole Symbol
bool isDynamic(void) const { return addr.isInvalid(); } ///< Is \b storage \e dynamic
@@ -122,7 +122,6 @@ public:
uintb getFirst(void) const { return addr.getOffset(); } ///< Get the first offset of \b this storage location
uintb getLast(void) const { return (addr.getOffset()+size-1); } ///< Get the last offset of \b this storage location
subsorttype getSubsort(void) const; ///< Get the sub-sort object
void initialize(const EntryInitData &data,uintb a,uintb b); ///< Fully initialize \b this
Symbol *getSymbol(void) const { return symbol; } ///< Get the Symbol associated with \b this
const Address &getAddr(void) const { return addr; } ///< Get the starting address of \b this storage
uint8 getHash(void) const { return hash; } ///< Get the hash used to identify \b this storage
@@ -819,13 +818,12 @@ private:
Address first; ///< The first address of the range
Address last; ///< The last address of the range
public:
ScopeMapper(void) {} ///< Constructor for use with rangemap
ScopeMapper(const inittype &data,const Address &f,const Address &l) {
scope = data; first = f; last = l; } ///< Initialize the range (with the owning Scope)
Address getFirst(void) const { return first; } ///< Get the first address in the range
Address getLast(void) const { return last; } ///< Get the last address in the range
NullSubsort getSubsort(void) const { return NullSubsort(); } ///< Get the sub-subsort object
Scope *getScope(void) const { return scope; } ///< Get the Scope owning this address range
void initialize(const inittype &data,const Address &f,const Address &l) {
scope = data; first = f; last = l; } ///< Initialize the range (with the owning Scope)
};
typedef rangemap<ScopeMapper> ScopeResolve; ///< A map from address to the owning Scope
@@ -1203,14 +1203,14 @@ void FlowInfo::injectUserOp(PcodeOp *op)
icontext.nextaddr = icontext.baseaddr;
for(int4 i=1;i<op->numInput();++i) { // Skip the first operand containing the injectid
Varnode *vn = op->getIn(i);
icontext.inputlist.push_back(VarnodeData());
icontext.inputlist.emplace_back();
icontext.inputlist.back().space = vn->getSpace();
icontext.inputlist.back().offset = vn->getOffset();
icontext.inputlist.back().size = vn->getSize();
}
Varnode *outvn = op->getOut();
if (outvn != (Varnode *)0) {
icontext.output.push_back(VarnodeData());
icontext.output.emplace_back();
icontext.output.back().space = outvn->getSpace();
icontext.output.back().offset = outvn->getOffset();
icontext.output.back().size = outvn->getSize();
@@ -545,7 +545,7 @@ void ParamListStandard::assignMap(const vector<Datatype *> &proto,bool isinput,T
throw ParamUnassignedError("Cannot assign parameter address for " + res.back().type->getName());
}
for(int4 i=1;i<proto.size();++i) {
res.push_back(ParameterPieces());
res.emplace_back();
if ((pointermax != 0)&&(proto[i]->getSize() > pointermax)) { // Datatype is too big
// Assume datatype is stored elsewhere and only the pointer is passed
AddrSpace *spc = spacebase;
@@ -567,7 +567,7 @@ void ParamListStandard::assignMap(const vector<Datatype *> &proto,bool isinput,T
}
}
else {
res.push_back(ParameterPieces());
res.emplace_back();
if (proto[0]->getMetatype() != TYPE_VOID) {
res.back().addr = assignAddress(proto[0],status);
if (res.back().addr.isInvalid())
@@ -1050,7 +1050,7 @@ void ParamListStandard::restoreXml(const Element *el,const AddrSpaceManager *man
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
const Element *subel = *fiter;
if (subel->getName() == "pentry") {
entry.push_back(ParamEntry(numgroup));
entry.emplace_back(numgroup);
entry.back().restoreXml(subel,manage,normalstack);
if (entry.back().getType()==TYPE_FLOAT) {
if (nonfloatgroup >= 0)
@@ -1089,7 +1089,7 @@ void ParamListStandardOut::assignMap(const vector<Datatype *> &proto,bool isinpu
vector<int4> status(numgroup,0);
// This is always an output list so we ignore -isinput-
res.push_back(ParameterPieces());
res.emplace_back();
res.back().type = proto[0];
res.back().flags = 0;
if (proto[0]->getMetatype() == TYPE_VOID) {
@@ -1109,8 +1109,8 @@ void ParamListStandardOut::assignMap(const vector<Datatype *> &proto,bool isinpu
res.back().type = pointertp;
res.back().flags = ParameterPieces::indirectstorage;
res.push_back(ParameterPieces()); // Add extra storage location in the input params
res.back().type = pointertp; // that holds a pointer to where the return value should be stored
res.emplace_back(); // Add extra storage location in the input params
res.back().type = pointertp; // that holds a pointer to where the return value should be stored
// leave its address invalid, to be filled in by the input list assignMap
res.back().flags = ParameterPieces::hiddenretparm; // Mark it as special
}
@@ -1851,7 +1851,7 @@ void ProtoModel::assignParameterStorage(const vector<Datatype *> &typelist,vecto
}
catch(ParamUnassignedError &err) {
res.clear();
res.push_back(ParameterPieces());
res.emplace_back();
// leave address undefined
res.back().flags = 0;
res.back().type = glb->types->getTypeVoid();
@@ -1984,7 +1984,7 @@ void ProtoModel::restoreXml(const Element *el)
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
effectlist.push_back(EffectRecord());
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::unaffected,*fiter,glb);
}
}
@@ -1992,7 +1992,7 @@ void ProtoModel::restoreXml(const Element *el)
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
effectlist.push_back(EffectRecord());
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::killedbycall,*fiter,glb);
}
}
@@ -2000,7 +2000,7 @@ void ProtoModel::restoreXml(const Element *el)
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
effectlist.push_back(EffectRecord());
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::return_address,*fiter,glb);
}
sawretaddr = true;
@@ -2029,7 +2029,7 @@ void ProtoModel::restoreXml(const Element *el)
const List &flist(subnode->getChildren());
List::const_iterator fiter;
for(fiter=flist.begin();fiter!=flist.end();++fiter) {
likelytrash.push_back(VarnodeData());
likelytrash.emplace_back();
likelytrash.back().restoreXml(*fiter,glb);
}
}
@@ -2088,7 +2088,7 @@ void ScoreProtoModel::addParameter(const Address &addr,int4 sz)
else
isparam = model->possibleOutputParamWithSlot(addr,sz,slot,slotsize);
if (isparam) {
entry.push_back(PEntry());
entry.emplace_back();
entry.back().origIndex = orig;
entry.back().slot = slot;
entry.back().size = slotsize;
@@ -2731,7 +2731,7 @@ void ProtoStoreInternal::clearOutput(void)
{
if (outparam != (ProtoParameter *)0)
delete outparam;
outparam = new ParameterBasic("",Address(),voidtype,0);
outparam = new ParameterBasic(voidtype);
}
ProtoParameter *ProtoStoreInternal::getOutput(void)
@@ -2852,7 +2852,7 @@ void ProtoStoreInternal::restoreXml(const Element *el,ProtoModel *model)
}
if ((flags & ParameterPieces::hiddenretparm) == 0)
namelist.push_back(name);
pieces.push_back(ParameterPieces());
pieces.emplace_back();
ParameterPieces &curparam( pieces.back() );
const List &sublist(subel->getChildren());
List::const_iterator subiter;
@@ -3963,7 +3963,7 @@ void FuncProto::restoreXml(const Element *el,Architecture *glb)
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
effectlist.push_back(EffectRecord());
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::unaffected,*iter2,glb);
++iter2;
}
@@ -3972,7 +3972,7 @@ void FuncProto::restoreXml(const Element *el,Architecture *glb)
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
effectlist.push_back(EffectRecord());
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::killedbycall,*iter2,glb);
++iter2;
}
@@ -3981,7 +3981,7 @@ void FuncProto::restoreXml(const Element *el,Architecture *glb)
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
effectlist.push_back(EffectRecord());
effectlist.emplace_back();
effectlist.back().restoreXml(EffectRecord::return_address,*iter2,glb);
++iter2;
}
@@ -3990,7 +3990,7 @@ void FuncProto::restoreXml(const Element *el,Architecture *glb)
const List &list2((*iter)->getChildren());
List::const_iterator iter2 = list2.begin();
while(iter2 != list2.end()) {
likelytrash.push_back(VarnodeData());
likelytrash.emplace_back();
likelytrash.back().restoreXml(*iter2,glb);
++iter2;
}
@@ -130,8 +130,7 @@ public:
typedef SubsortPosition subsorttype; ///< The sub-sort object for a rangemap
typedef InitData inittype; ///< Initialization data for a ScopeMapper
ParamEntryRange(void) {} ///< Constructor for use with rangemap
void initialize(const inittype &data,uintb f,uintb l) {
ParamEntryRange(const inittype &data,uintb f,uintb l) {
first = f; last = l; position = data.position; entry = data.entry; } ///< Initialize the range
uintb getFirst(void) const { return first; } ///< Get the first address in the range
uintb getLast(void) const { return last; } ///< Get the last address in the range
@@ -981,6 +980,8 @@ class ParameterBasic : public ProtoParameter {
public:
ParameterBasic(const string &nm,const Address &ad,Datatype *tp,uint4 fl) {
name = nm; addr = ad; type = tp; flags=fl; } ///< Construct from components
ParameterBasic(Datatype *tp) {
type = tp; flags = 0; } ///< Construct a \e void parameter
virtual const string &getName(void) const { return name; }
virtual Datatype *getType(void) const { return type; }
virtual Address getAddress(void) const { return addr; }
@@ -482,6 +482,8 @@ public:
/// \brief End of all (alive) PcodeOp objects attached to a specific Address
PcodeOpTree::const_iterator endOp(const Address &addr) const { return obank.end(addr); }
bool moveRespectingCover(PcodeOp *op,PcodeOp *lastOp); ///< Move given op past \e lastOp respecting covers if possible
// Jumptable routines
JumpTable *linkJumpTable(PcodeOp *op); ///< Link jump-table with a given BRANCHIND
JumpTable *findJumpTable(const PcodeOp *op) const; ///< Find a jump-table associated with a given BRANCHIND
@@ -1361,3 +1361,54 @@ void cseEliminateList(Funcdata &data,vector< pair<uintm,PcodeOp *> > &list,vecto
liter2++;
}
}
/// This routine should be called only after Varnode merging and explicit/implicit attributes have
/// been calculated. Determine if the given op can be moved (only within its basic block) to
/// after \e lastOp. The output of any PcodeOp moved across must not be involved, directly or
/// indirectly, with any variable in the expression rooted at the given op.
/// If the move is possible, perform the move.
/// \param op is the given PcodeOp
/// \param lastOp is the PcodeOp to move past
/// \return \b true if the move is possible
bool Funcdata::moveRespectingCover(PcodeOp *op,PcodeOp *lastOp)
{
if (op == lastOp) return true; // Nothing to move past
if (op->isCall()) return false;
PcodeOp *prevOp = (PcodeOp *)0;
if (op->code() == CPUI_CAST) {
Varnode *vn = op->getIn(0);
if (!vn->isExplicit()) { // If CAST is part of expression, we need to move the previous op as well
if (!vn->isWritten()) return false;
prevOp = vn->getDef();
if (prevOp->isCall()) return false;
if (op->previousOp() != prevOp) return false; // Previous op must exist and feed into the CAST
}
}
Varnode *rootvn = op->getOut();
vector<HighVariable *> highList;
int4 typeVal = HighVariable::markExpression(rootvn, highList);
PcodeOp *curOp = op;
do {
PcodeOp *nextOp = curOp->nextOp();
OpCode opc = nextOp->code();
if (opc != CPUI_COPY && opc != CPUI_CAST) break; // Limit ourselves to only crossing COPY and CAST ops
if (rootvn == nextOp->getIn(0)) break; // Data-flow order dependence
Varnode *copyVn = nextOp->getOut();
if (copyVn->getHigh()->isMark()) break; // Direct interference: COPY writes what original op reads
if (typeVal != 0 && copyVn->isAddrTied()) break; // Possible indirect interference
curOp = nextOp;
} while(curOp != lastOp);
for(int4 i=0;i<highList.size();++i) // Clear marks on expression
highList[i]->clearMark();
if (curOp == lastOp) { // If we are able to cross everything
opUninsert(op); // Move -op-
opInsertAfter(op, lastOp);
if (prevOp != (PcodeOp *)0) { // If there was a CAST, move both ops
opUninsert(prevOp);
opInsertAfter(prevOp, lastOp);
}
return true;
}
return false;
}
@@ -731,38 +731,35 @@ void Funcdata::clearDeadVarnodes(void)
void Funcdata::calcNZMask(void)
{
vector<PcodeOp *> opstack;
vector<int4> slotstack;
vector<PcodeOpNode> opstack;
list<PcodeOp *>::const_iterator oiter;
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
PcodeOp *op = *oiter;
if (op->isMark()) continue;
opstack.push_back(op);
slotstack.push_back(0);
opstack.push_back(PcodeOpNode(op,0));
op->setMark();
do {
// Get next edge
op = opstack.back();
int4 slot = slotstack.back();
if (slot >= op->numInput()) { // If no edge left
Varnode *outvn = op->getOut();
PcodeOpNode &node( opstack.back() );
if (node.slot >= node.op->numInput()) { // If no edge left
Varnode *outvn = node.op->getOut();
if (outvn != (Varnode *)0) {
outvn->nzm = op->getNZMaskLocal(true);
outvn->nzm = node.op->getNZMaskLocal(true);
}
opstack.pop_back(); // Pop a level
slotstack.pop_back();
continue;
}
slotstack.back() = slot + 1; // Advance to next input
int4 oldslot = node.slot;
node.slot += 1; // Advance to next input
// Determine if we want to traverse this edge
if (op->code() == CPUI_MULTIEQUAL) {
if (op->getParent()->isLoopIn(slot)) // Clip looping edges
if (node.op->code() == CPUI_MULTIEQUAL) {
if (node.op->getParent()->isLoopIn(oldslot)) // Clip looping edges
continue;
}
// Traverse edge indicated by slot
Varnode *vn = op->getIn(slot);
Varnode *vn = node.op->getIn(oldslot);
if (!vn->isWritten()) {
if (vn->isConstant())
vn->nzm = vn->getOffset();
@@ -773,32 +770,32 @@ void Funcdata::calcNZMask(void)
}
}
else if (!vn->getDef()->isMark()) { // If haven't traversed before
opstack.push_back(vn->getDef());
slotstack.push_back(0);
opstack.push_back(PcodeOpNode(vn->getDef(),0));
vn->getDef()->setMark();
}
} while(!opstack.empty());
}
vector<PcodeOp *> worklist;
// Clear marks and push ops with looping edges onto worklist
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
PcodeOp *op = *oiter;
op->clearMark();
if (op->code() == CPUI_MULTIEQUAL)
opstack.push_back(op);
worklist.push_back(op);
}
// Continue to propagate changes along all edges
while(!opstack.empty()) {
PcodeOp *op = opstack.back();
opstack.pop_back();
while(!worklist.empty()) {
PcodeOp *op = worklist.back();
worklist.pop_back();
Varnode *vn = op->getOut();
if (vn == (Varnode *)0) continue;
uintb nzmask = op->getNZMaskLocal(false);
if (nzmask != vn->nzm) {
vn->nzm = nzmask;
for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter)
opstack.push_back(*oiter);
worklist.push_back(*oiter);
}
}
}
@@ -97,7 +97,7 @@ void ContextDatabase::restoreTracked(const Element *el,const AddrSpaceManager *m
while(iter != list.end()) {
const Element *subel = *iter;
vec.push_back(TrackedContext());
vec.emplace_back();
vec.back().restoreXml(subel,manage);
++iter;
}
@@ -811,7 +811,7 @@ void Heritage::generateLoadGuard(StackNode &node,PcodeOp *op,AddrSpace *spc)
{
if (!op->usesSpacebasePtr()) {
loadGuard.push_back(LoadGuard());
loadGuard.emplace_back();
loadGuard.back().set(op,spc,node.offset);
fd->opMarkSpacebasePtr(op);
}
@@ -828,7 +828,7 @@ void Heritage::generateStoreGuard(StackNode &node,PcodeOp *op,AddrSpace *spc)
{
if (!op->usesSpacebasePtr()) {
storeGuard.push_back(LoadGuard());
storeGuard.emplace_back();
storeGuard.back().set(op,spc,node.offset);
fd->opMarkSpacebasePtr(op);
}
@@ -2345,7 +2345,7 @@ void IfcPreferSplit::execute(istream &s)
s >> dec >> split;
if (split == -1)
throw IfaceParseError("Bad split offset");
dcp->conf->splitrecords.push_back(PreferSplitRecord());
dcp->conf->splitrecords.emplace_back();
PreferSplitRecord &rec( dcp->conf->splitrecords.back() );
rec.storage.space = addr.getSpace();
@@ -506,39 +506,33 @@ uintb JumpBasic::backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode
void JumpBasic::findDeterminingVarnodes(PcodeOp *op,int4 slot)
{
vector<PcodeOp *> path;
vector<int4> slotpath;
PcodeOp *curop;
Varnode *curvn;
vector<PcodeOpNode> path;
bool firstpoint = false; // Have not seen likely switch variable yet
path.push_back(op);
slotpath.push_back(slot);
path.push_back(PcodeOpNode(op,slot));
do { // Traverse through tree of inputs to final address
curop = path.back();
curvn = curop->getIn(slotpath.back());
PcodeOpNode &node(path.back());
Varnode *curvn = node.op->getIn(node.slot);
if (isprune(curvn)) { // Here is a node of the tree
if (ispoint(curvn)) { // Is it a possible switch variable
if (!firstpoint) { // If it is the first possible
pathMeld.set(path,slotpath); // Take the current path as the result
pathMeld.set(path); // Take the current path as the result
firstpoint = true;
}
else // If we have already seen at least one possible
pathMeld.meld(path,slotpath);
pathMeld.meld(path);
}
slotpath.back() += 1;
while(slotpath.back() >= path.back()->numInput()) {
path.back().slot += 1;
while(path.back().slot >= path.back().op->numInput()) {
path.pop_back();
slotpath.pop_back();
if (path.empty()) break;
slotpath.back() += 1;
path.back().slot += 1;
}
}
else { // This varnode is not pruned
path.push_back(curvn->getDef());
slotpath.push_back(0);
path.push_back(PcodeOpNode(curvn->getDef(),0));
}
} while(path.size() > 1);
if (pathMeld.empty()) { // Never found a likely point, which means that
@@ -785,7 +779,7 @@ void PathMeld::internalIntersect(vector<int4> &parentMap)
/// \param cutOff is the number of PcodeOps with an input in the common path
/// \param parentMap is the map from old common Varnodes to the new common Varnodes
/// \return the index of the last (earliest) Varnode in the common path or -1
int4 PathMeld::meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<int4> &parentMap)
int4 PathMeld::meldOps(const vector<PcodeOpNode> &path,int4 cutOff,const vector<int4> &parentMap)
{
// First update opMeld.rootVn with new intersection information
@@ -804,7 +798,7 @@ int4 PathMeld::meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<in
int4 meldPos = 0; // Ops moved from old opMeld into newMeld
const BlockBasic *lastBlock = (const BlockBasic *)0;
for(int4 i=0;i<cutOff;++i) {
PcodeOp *op = path[i]; // Current op in the new path
PcodeOp *op = path[i].op; // Current op in the new path
PcodeOp *curOp = (PcodeOp *)0;
while(meldPos < opMeld.size()) {
PcodeOp *trialOp = opMeld[meldPos].op; // Current op in the old opMeld
@@ -874,15 +868,14 @@ void PathMeld::set(const PathMeld &op2)
}
/// This container is initialized to hold a single data-flow path.
/// \param path is the list of PcodeOps in the path (in reverse execution order)
/// \param slot is the list of each Varnode presented as an input slot in the corresponding PcodeOp
void PathMeld::set(const vector<PcodeOp *> &path,const vector<int4> &slot)
/// \param path is the list of PcodeOpNode edges in the path (in reverse execution order)
void PathMeld::set(const vector<PcodeOpNode> &path)
{
for(int4 i=0;i<path.size();++i) {
PcodeOp *op = path[i];
Varnode *vn = op->getIn(slot[i]);
opMeld.push_back(RootedOp(op,i));
const PcodeOpNode &node(path[i]);
Varnode *vn = node.op->getIn(node.slot);
opMeld.push_back(RootedOp(node.op,i));
commonVn.push_back(vn);
}
}
@@ -921,23 +914,23 @@ void PathMeld::clear(void)
/// Add the new path, recalculating the set of Varnodes common to all paths.
/// Paths are trimmed to ensure that any path that splits from the common intersection
/// must eventually rejoin.
/// \param path is the new path of PcodeOps to meld, in reverse execution order
/// \param slot is the set of Varnodes in the new path presented as input slots to the corresponding PcodeOp
void PathMeld::meld(vector<PcodeOp *> &path,vector<int4> &slot)
/// \param path is the new path of PcodeOpNode edges to meld, in reverse execution order
void PathMeld::meld(vector<PcodeOpNode> &path)
{
vector<int4> parentMap;
for(int4 i=0;i<path.size();++i) {
Varnode *vn = path[i]->getIn(slot[i]);
vn->setMark(); // Mark varnodes in the new path, so its easy to see intersection
PcodeOpNode &node(path[i]);
node.op->getIn(node.slot)->setMark(); // Mark varnodes in the new path, so its easy to see intersection
}
internalIntersect(parentMap); // Calculate varnode intersection, and map from old intersection -> new
int4 cutOff = -1;
// Calculate where the cutoff point is in the new path
for(int4 i=0;i<path.size();++i) {
Varnode *vn = path[i]->getIn(slot[i]);
PcodeOpNode &node(path[i]);
Varnode *vn = node.op->getIn(node.slot);
if (!vn->isMark()) { // If mark already cleared, we know it is in intersection
cutOff = i + 1; // Cut-off must at least be past this -vn-
}
@@ -948,7 +941,6 @@ void PathMeld::meld(vector<PcodeOp *> &path,vector<int4> &slot)
if (newCutoff >= 0) // If not all ops could be ordered
truncatePaths(newCutoff); // Cut off at the point where we couldn't order
path.resize(cutOff);
slot.resize(cutOff);
}
/// The starting Varnode, common to all paths, is provided as an index.
@@ -2668,7 +2660,7 @@ void JumpTable::restoreXml(const Element *el)
missedlabel = true; // No following entries are allowed to have a label attribute
}
else if (subel->getName() == "loadtable") {
loadpoints.push_back(LoadTable());
loadpoints.emplace_back();
loadpoints.back().restoreXml(subel,glb);
}
else if (subel->getName() == "basicoverride") {
@@ -73,15 +73,15 @@ class PathMeld {
vector<Varnode *> commonVn; ///< Varnodes in common with all paths
vector<RootedOp> opMeld; ///< All the ops for the melded paths
void internalIntersect(vector<int4> &parentMap);
int4 meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<int4> &parentMap);
int4 meldOps(const vector<PcodeOpNode> &path,int4 cutOff,const vector<int4> &parentMap);
void truncatePaths(int4 cutPoint);
public:
void set(const PathMeld &op2); ///< Copy paths from another container
void set(const vector<PcodeOp *> &path,const vector<int4> &slot); ///< Initialize \b this to be a single path
void set(const vector<PcodeOpNode> &path); ///< Initialize \b this to be a single path
void set(PcodeOp *op,Varnode *vn); ///< Initialize \b this container to a single node "path"
void append(const PathMeld &op2); ///< Append a new set of paths to \b this set of paths
void clear(void); ///< Clear \b this to be an empty container
void meld(vector<PcodeOp *> &path,vector<int4> &slot); ///< Meld a new path into \b this container
void meld(vector<PcodeOpNode> &path); ///< Meld a new path into \b this container
void markPaths(bool val,int4 startVarnode); ///< Mark PcodeOps paths from the given start
int4 numCommonVarnode(void) const { return commonVn.size(); } ///< Return the number of Varnodes common to all paths
int4 numOps(void) const { return opMeld.size(); } ///< Return the number of PcodeOps across all paths
@@ -171,6 +171,106 @@ bool PcodeOp::isCseMatch(const PcodeOp *op) const
return true;
}
/// Its possible for the order of operations to be rearranged in some instances but still keep
/// equivalent data-flow. Test if \b this operation can be moved to occur immediately after
/// a specified \e point operation. This currently only tests for movement within a basic block.
/// \param point is the specified point to move \b this after
/// \return \b true if the move is possible
bool PcodeOp::isMoveable(const PcodeOp *point) const
{
if (this == point) return true; // No movement necessary
bool movingLoad = false;
if (getEvalType() == PcodeOp::special) {
if (code() == CPUI_LOAD)
movingLoad = true; // Allow LOAD to be moved with additional restrictions
else
return false; // Don't move special ops
}
if (parent != point->parent) return false; // Not in the same block
if (output != (Varnode *)0) {
// Output cannot be moved past an op that reads it
list<PcodeOp *>::const_iterator iter = output->beginDescend();
list<PcodeOp *>::const_iterator enditer = output->endDescend();
while(iter != enditer) {
PcodeOp *readOp = *iter;
++iter;
if (readOp->parent != parent) continue;
if (readOp->start.getOrder() <= point->start.getOrder())
return false; // Is in the block and is read before (or at) -point-
}
}
// Only allow this op to be moved across a CALL in very restrictive circumstances
bool crossCalls = false;
if (getEvalType() != PcodeOp::special) {
// Check for a normal op where all inputs and output are not address tied
if (output != (Varnode *)0 && !output->isAddrTied() && !output->isPersist()) {
int4 i;
for(i=0;i<numInput();++i) {
const Varnode *vn = getIn(i);
if (vn->isAddrTied() || vn->isPersist())
break;
}
if (i == numInput())
crossCalls = true;
}
}
vector<const Varnode *> tiedList;
for(int4 i=0;i<numInput();++i) {
const Varnode *vn = getIn(i);
if (vn->isAddrTied())
tiedList.push_back(vn);
}
list<PcodeOp *>::iterator biter = basiciter;
do {
++biter;
PcodeOp *op = *biter;
if (op->getEvalType() == PcodeOp::special) {
switch (op->code()) {
case CPUI_LOAD:
if (output != (Varnode *)0) {
if (output->isAddrTied()) return false;
}
break;
case CPUI_STORE:
if (movingLoad)
return false;
else {
if (!tiedList.empty()) return false;
if (output != (Varnode *)0) {
if (output->isAddrTied()) return false;
}
}
break;
case CPUI_INDIRECT: // Let thru, deal with what's INDIRECTed around separately
case CPUI_SEGMENTOP:
case CPUI_CPOOLREF:
break;
case CPUI_CALL:
case CPUI_CALLIND:
case CPUI_NEW:
if (!crossCalls) return false;
break;
default:
return false;
}
}
if (op->output != (Varnode *)0) {
if (movingLoad) {
if (op->output->isAddrTied()) return false;
}
for(int4 i=0;i<tiedList.size();++i) {
const Varnode *vn = tiedList[i];
if (vn->overlap(*op->output)>=0)
return false;
if (op->output->overlap(*vn)>=0)
return false;
}
}
} while(biter != point->basiciter);
return true;
}
/// Set the behavioral class (opcode) of this operation. For most applications this should only be called
/// by the PcodeOpBank. This is fairly low-level but does cache various boolean flags associated with the opcode
/// \param t_op is the behavioural class to set
@@ -206,6 +206,7 @@ public:
bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); }
uintm getCseHash(void) const; ///< Return hash indicating possibility of common subexpression elimination
bool isCseMatch(const PcodeOp *op) const; ///< Return \b true if this and \e op represent common subexpressions
bool isMoveable(const PcodeOp *point) const; ///< Can \b this be moved to after \e point, without disturbing data-flow
TypeOp *getOpcode(void) const { return opcode; } ///< Get the opcode for this op
OpCode code(void) const { return opcode->getOpcode(); } ///< Get the opcode id (enum) for this op
bool isCommutative(void) const { return ((flags & PcodeOp::commutative)!=0); } ///< Return \b true if inputs commute
@@ -229,6 +230,16 @@ public:
bool inheritsSign(void) const { return opcode->inheritsSign(); } ///< Does this token inherit its sign from operands
};
/// \brief An edge in a data-flow path or graph
///
/// A minimal node for traversing expressions in the data-flow
struct PcodeOpNode {
PcodeOp *op; ///< The p-code end-point of the edge
int4 slot; ///< Slot indicating the input Varnode end-point of the edge
PcodeOpNode(void) { op = (PcodeOp *)0; slot = 0; } ///< Unused constructor
PcodeOpNode(PcodeOp *o,int4 s) { op = o; slot = s; } ///< Constructor
};
/// A map from sequence number (SeqNum) to PcodeOp
typedef map<SeqNum,PcodeOp *> PcodeOpTree;
@@ -57,6 +57,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
registerOption(new OptionErrorTooManyInstructions());
registerOption(new OptionDefaultPrototype());
registerOption(new OptionInferConstPtr());
registerOption(new OptionForLoops());
registerOption(new OptionInline());
registerOption(new OptionNoReturn());
registerOption(new OptionStructAlign());
@@ -245,6 +246,24 @@ string OptionInferConstPtr::apply(Architecture *glb,const string &p1,const strin
return res;
}
/// \class OptionForLoops
/// \brief Toggle whether the decompiler attempts to recover \e for-loop variables
///
/// Setting the first parameter to "on" causes the decompiler to search for a suitable loop variable
/// controlling iteration of a \e while-do block. The \e for-loop displays the following on a single line:
/// - loop variable initializer (optional)
/// - loop condition
/// - loop variable incrementer
///
string OptionForLoops::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{
glb->analyze_for_loops = onOrOff(p1);
string res = "Recovery of for-loops is " + p1;
return res;
}
/// \class OptionInline
/// \brief Mark/unmark a specific function as \e inline
///

Some files were not shown because too many files have changed in this diff Show More