mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 12:45:32 +08:00
Merge remote-tracking branch 'origin/master' into debugger
This commit is contained in:
+38
-42
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+2
-7
@@ -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
|
||||
|
||||
+10
-157
@@ -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();
|
||||
|
||||
+1
-1
@@ -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());
|
||||
|
||||
|
||||
+15
-25
@@ -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) {
|
||||
|
||||
+10
-19
@@ -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);
|
||||
}
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
+8
-8
@@ -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) {
|
||||
|
||||
+117
-2
@@ -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 {
|
||||
|
||||
|
||||
+135
-219
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -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
Reference in New Issue
Block a user