BitFields - added direct parse support of bitfield entry within

composite.  Restrict use of bitfield datatype.
This commit is contained in:
ghidra1 2019-05-14 14:30:41 -04:00
parent 5ac462441a
commit 1fcad78bc4
13 changed files with 153 additions and 43 deletions

View file

@ -239,7 +239,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
if (!dtc.isBitFieldComponent()) { if (!dtc.isBitFieldComponent()) {
throw new IllegalArgumentException("editOrdinal does not correspond to bitfield"); throw new IllegalArgumentException("editOrdinal does not correspond to bitfield");
} }
bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc), false); bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc));
setApplyEnabled(true); setApplyEnabled(true);
} }

View file

@ -193,7 +193,8 @@ public class BitFieldEditorPanel extends JPanel {
private JComponent createDataTypeChoiceEditor() { private JComponent createDataTypeChoiceEditor() {
dtChoiceEditor = new DataTypeSelectionEditor(dtmService, -1, AllowedDataTypes.BITFIELD_BASE_TYPE); dtChoiceEditor =
new DataTypeSelectionEditor(dtmService, -1, AllowedDataTypes.BITFIELD_BASE_TYPE);
dtChoiceEditor.setConsumeEnterKeyPress(false); dtChoiceEditor.setConsumeEnterKeyPress(false);
dtChoiceEditor.setTabCommitsEdit(true); dtChoiceEditor.setTabCommitsEdit(true);
//dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager()); //dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager());
@ -368,11 +369,8 @@ public class BitFieldEditorPanel extends JPanel {
* If null an allocation size of 4-bytes will be used but may be adjusted. * If null an allocation size of 4-bytes will be used but may be adjusted.
* @param bitfieldDtc bitfield component or null * @param bitfieldDtc bitfield component or null
* @param allocationOffset allocation offset to be used * @param allocationOffset allocation offset to be used
* @param useCurrentAllocation retain current allocation size, otherwise
* use size of base datatype.
*/ */
void initEdit(DataTypeComponent bitfieldDtc, int allocationOffset, void initEdit(DataTypeComponent bitfieldDtc, int allocationOffset) {
boolean useCurrentAllocation) {
String initialFieldName = null; String initialFieldName = null;
DataType initialBaseDataType = null; DataType initialBaseDataType = null;
int allocationSize = -1; int allocationSize = -1;
@ -387,9 +385,13 @@ public class BitFieldEditorPanel extends JPanel {
initialFieldName = bitfieldDtc.getFieldName(); initialFieldName = bitfieldDtc.getFieldName();
BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType(); BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType();
initialBaseDataType = bitfieldDt.getBaseDataType(); initialBaseDataType = bitfieldDt.getBaseDataType();
if (!useCurrentAllocation || allocationSize < 1) { if (allocationSize < 1) {
allocationSize = initialBaseDataType.getLength(); allocationSize = initialBaseDataType.getLength();
} }
int allocationAdjust = composite.getLength() - allocationOffset - allocationSize;
if (allocationAdjust < 0) {
allocationSize += allocationAdjust;
}
} }
if (allocationSize < 1) { if (allocationSize < 1) {
allocationSize = 4; allocationSize = 4;

View file

@ -1216,6 +1216,7 @@ public abstract class CompositeEditorPanel extends JPanel
private DataTypeSelectionEditor editor; private DataTypeSelectionEditor editor;
private DataType dt; private DataType dt;
private int maxLength; private int maxLength;
private boolean bitfieldAllowed;
private JPanel editorPanel; private JPanel editorPanel;
@ -1224,6 +1225,7 @@ public abstract class CompositeEditorPanel extends JPanel
boolean isSelected, int row, int column) { boolean isSelected, int row, int column) {
model.clearStatus(); model.clearStatus();
maxLength = model.getMaxAddLength(row); maxLength = model.getMaxAddLength(row);
bitfieldAllowed = model.isBitFieldAllowed();
init(); init();
DataTypeInstance dti = (DataTypeInstance) value; DataTypeInstance dti = (DataTypeInstance) value;
@ -1243,7 +1245,9 @@ public abstract class CompositeEditorPanel extends JPanel
Plugin plugin = provider.getPlugin(); Plugin plugin = provider.getPlugin();
final PluginTool tool = plugin.getTool(); final PluginTool tool = plugin.getTool();
editor = new DataTypeSelectionEditor(tool, maxLength, AllowedDataTypes.SIZABLE_DYNAMIC); editor = new DataTypeSelectionEditor(tool, maxLength,
bitfieldAllowed ? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD
: AllowedDataTypes.SIZABLE_DYNAMIC);
editor.setTabCommitsEdit(true); editor.setTabCommitsEdit(true);
DataTypeManager originalDataTypeManager = model.getOriginalDataTypeManager(); DataTypeManager originalDataTypeManager = model.getOriginalDataTypeManager();
editor.setPreferredDataTypeManager(originalDataTypeManager); editor.setPreferredDataTypeManager(originalDataTypeManager);

View file

@ -87,6 +87,11 @@ public interface EditorModel {
*/ */
public boolean isArrayAllowed(); public boolean isArrayAllowed();
/**
* Returns whether or not a bitfield is allowed at the current location.
*/
public boolean isBitFieldAllowed();
/** /**
* Returns whether or not clearing the selected components is allowed. * Returns whether or not clearing the selected components is allowed.
*/ */

View file

@ -181,8 +181,7 @@ class StructureEditorModel extends CompEditorModel {
@Override @Override
public DataTypeComponent getComponent(int rowIndex) { public DataTypeComponent getComponent(int rowIndex) {
int numComponents = getNumComponents(); int numComponents = getNumComponents();
if (numComponents == 0 || rowIndex < 0 || rowIndex == numComponents) {
if (rowIndex < 0 || rowIndex == numComponents) {
return null; return null;
} }
Structure viewStruct = (Structure) viewComposite; Structure viewStruct = (Structure) viewComposite;
@ -571,6 +570,11 @@ class StructureEditorModel extends CompEditorModel {
// Begin methods for determining if a type of edit action is allowed. // Begin methods for determining if a type of edit action is allowed.
// ************************************************************* // *************************************************************
@Override
public boolean isBitFieldAllowed() {
return isSingleRowSelection() && !isFlexibleArraySelection();
}
/** /**
* Returns whether or not the selection * Returns whether or not the selection
* is allowed to be changed into an array. * is allowed to be changed into an array.

View file

@ -213,13 +213,18 @@ class UnionEditorModel extends CompEditorModel {
// Begin methods for determining if a type of edit action is allowed. // Begin methods for determining if a type of edit action is allowed.
// ************************************************************* // *************************************************************
@Override
public boolean isBitFieldAllowed() {
return isSingleRowSelection();
}
/** /**
* Returns whether or not the selection * Returns whether or not the selection
* is allowed to be changed into an array. * is allowed to be changed into an array.
*/ */
@Override @Override
public boolean isArrayAllowed() { public boolean isArrayAllowed() {
return (getNumSelectedComponentRows() == 1); return isSingleRowSelection();
} }
/** /**

View file

@ -687,9 +687,14 @@ class StackEditorModel extends CompositeEditorModel {
return true; return true;
} }
@Override
public boolean isBitFieldAllowed() {
return false;
}
@Override @Override
public boolean isArrayAllowed() { public boolean isArrayAllowed() {
if (getNumSelectedRows() != 1) { if (getNumSelectedRows() != 1 || viewComposite == null) {
return false; return false;
} }
int index = getMinIndexSelected(); int index = getMinIndexSelected();
@ -1437,11 +1442,17 @@ class StackEditorModel extends CompositeEditorModel {
@Override @Override
public DataTypeComponent getComponent(int rowIndex) { public DataTypeComponent getComponent(int rowIndex) {
if (viewComposite == null) {
return null;
}
return viewComposite.getComponent(rowIndex); return viewComposite.getComponent(rowIndex);
} }
@Override @Override
public int getNumComponents() { public int getNumComponents() {
if (viewComposite == null) {
return 0;
}
return viewComposite.getNumComponents(); return viewComposite.getNumComponents();
} }

View file

@ -298,7 +298,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// if it is not a known type, the prompt user to create new one // if it is not a known type, the prompt user to create new one
if (!isValidDataType()) { if (!isValidDataType()) {
return promptUserToCreateDataType(); return parseDataTypeTextEntry();
} }
return true; return true;
@ -339,8 +339,12 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
return null; return null;
} }
// TODO: implement in the future to allow the user to create data types /**
private boolean promptUserToCreateDataType() throws InvalidDataTypeException { * Parse datatype text entry using {@link DataTypeParser}. Allows addition
* of supported modifiers (e.g., arrays, pointers, etc.).
* @return true if parse successful else false
*/
private boolean parseDataTypeTextEntry() throws InvalidDataTypeException {
if (selectionField.getText().trim().length() == 0) { if (selectionField.getText().trim().length() == 0) {
// no need to invoke parser on empty string // no need to invoke parser on empty string
@ -349,9 +353,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// we will create new pointer and array types by default // we will create new pointer and array types by default
DataType newDataType = null; DataType newDataType = null;
// try { DataTypeParser parser = new DataTypeParser(dataTypeManager, dataTypeManager,
DataTypeParser parser = dataTypeManagerService, allowedDataTypes);
new DataTypeParser(dataTypeManager, null, dataTypeManagerService, allowedDataTypes);
newDataType = parser.parse(selectionField.getText(), getDataTypeRootForCurrentText()); newDataType = parser.parse(selectionField.getText(), getDataTypeRootForCurrentText());
if (newDataType != null) { if (newDataType != null) {
if (maxSize >= 0 && newDataType.getLength() > newDataType.getLength()) { if (maxSize >= 0 && newDataType.getLength() > newDataType.getLength()) {
@ -360,23 +363,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
selectionField.setSelectedValue(newDataType); selectionField.setSelectedValue(newDataType);
return true; return true;
} }
// }
// // squash these exceptions, as this method returns false if we were unable to create the
// // given data type
// catch ( CancelledException ce ) {
// }
// prompt user
/*
int userChoice = JOptionPane.showOptionDialog( selectionField,
"Data type \"" + selectionField.getText() + "\" does not exist. Would you " +
"like to create it?", "Create New Data Type?",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null );
if ( userChoice == JOptionPane.YES_OPTION ) {
return createNewDataTypeForUserSelection();
}
*/
return false; return false;
} }

View file

@ -23,6 +23,7 @@ import ghidra.app.services.DataTypeManagerService;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
public class DataTypeParser { public class DataTypeParser {
@ -39,6 +40,15 @@ public class DataTypeParser {
* All fixed-length data-types and sizable Dynamic(i.e., canSpecifyLength) data-types * All fixed-length data-types and sizable Dynamic(i.e., canSpecifyLength) data-types
*/ */
SIZABLE_DYNAMIC, SIZABLE_DYNAMIC,
/**
* All fixed-length data-types, sizable Dynamic data-types.
* In addition a bitfield specification may be specified (e.g., int:2)
* for use when defining structure and union components only
* (see {@link ProxyBitFieldDataType}). Parser must be properly constructed
* with the intended {@link DataTypeParser#destinationDataTypeManager}.
* If a bitfield is returned special handling is required.
*/
SIZABLE_DYNAMIC_AND_BITFIELD,
/** /**
* Only Fixed-length data-types * Only Fixed-length data-types
*/ */
@ -54,6 +64,26 @@ public class DataTypeParser {
BITFIELD_BASE_TYPE BITFIELD_BASE_TYPE
} }
/**
* <code>ProxyBitFieldDataType</code> provides acts as a proxy bitfield
* whose specification may be used when defining a structure or
* union bitfield. This datatype may not be directly applied to a program.
*/
private static class ProxyBitFieldDataType extends BitFieldDataType {
/**
* Construct proxy bitfield datatype for use when defining
* a structure or union bitfield.
* @param baseDataType a supported primitive integer data type or TypeDef to such a type.
* A deep clone of this type will be performed using the specified dataMgr.
* @param bitSize size of bit-field expressed as number of bits
* @throws InvalidDataTypeException if specified baseDataType is not permitted
*/
private ProxyBitFieldDataType(DataType baseDataType, int bitSize)
throws InvalidDataTypeException {
super(baseDataType, bitSize);
}
}
private DataTypeManager sourceDataTypeManager; // may be null private DataTypeManager sourceDataTypeManager; // may be null
private DataTypeManager destinationDataTypeManager; // may be null private DataTypeManager destinationDataTypeManager; // may be null
private DataTypeManagerService dataTypeManagerService; // may be null private DataTypeManagerService dataTypeManagerService; // may be null
@ -162,6 +192,12 @@ public class DataTypeParser {
*/ */
public static void ensureIsAllowableType(DataType dt, AllowedDataTypes allowedTypes) public static void ensureIsAllowableType(DataType dt, AllowedDataTypes allowedTypes)
throws InvalidDataTypeException { throws InvalidDataTypeException {
if (dt instanceof BitFieldDataType) {
if (allowedTypes != AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD) {
throw new InvalidDataTypeException("bitfield data-type not allowed");
}
return;
}
switch (allowedTypes) { switch (allowedTypes) {
case DYNAMIC: case DYNAMIC:
if (dt instanceof FactoryDataType) { if (dt instanceof FactoryDataType) {
@ -169,6 +205,7 @@ public class DataTypeParser {
} }
break; break;
case SIZABLE_DYNAMIC: case SIZABLE_DYNAMIC:
case SIZABLE_DYNAMIC_AND_BITFIELD:
if (dt instanceof FactoryDataType) { if (dt instanceof FactoryDataType) {
throw new InvalidDataTypeException("factory data-type not allowed"); throw new InvalidDataTypeException("factory data-type not allowed");
} }
@ -205,7 +242,11 @@ public class DataTypeParser {
throws InvalidDataTypeException { throws InvalidDataTypeException {
int arraySequenceStartIndex = -1; int arraySequenceStartIndex = -1;
List<DtPiece> modifiers = new ArrayList<>(); List<DtPiece> modifiers = new ArrayList<>();
boolean terminalModifier = false;
for (String piece : splitDataTypeModifiers(dataTypeModifiers)) { for (String piece : splitDataTypeModifiers(dataTypeModifiers)) {
if (terminalModifier) {
throw new InvalidDataTypeException("Invalid data type modifier");
}
if (piece.startsWith("*")) { if (piece.startsWith("*")) {
modifiers.add(new PointerSpecPiece(piece)); modifiers.add(new PointerSpecPiece(piece));
arraySequenceStartIndex = -1; arraySequenceStartIndex = -1;
@ -221,6 +262,10 @@ public class DataTypeParser {
modifiers.add(arraySpec); modifiers.add(arraySpec);
} }
} }
else if (piece.startsWith(":")) {
terminalModifier = true;
modifiers.add(new BitfieldSpecPiece(piece));
}
else if (piece.startsWith("{")) { else if (piece.startsWith("{")) {
// # indicates the size of an array element when the base data type is dynamic. // # indicates the size of an array element when the base data type is dynamic.
modifiers.add(new ElementSizeSpecPiece(piece)); modifiers.add(new ElementSizeSpecPiece(piece));
@ -241,11 +286,22 @@ public class DataTypeParser {
elementLength = ((ElementSizeSpecPiece) modifier).getElementSize(); elementLength = ((ElementSizeSpecPiece) modifier).getElementSize();
} }
} }
else { else if (modifier instanceof ArraySpecPiece) {
int elementCount = ((ArraySpecPiece) modifier).getElementCount(); int elementCount = ((ArraySpecPiece) modifier).getElementCount();
dt = createArrayDataType(dt, elementLength, elementCount); dt = createArrayDataType(dt, elementLength, elementCount);
elementLength = dt.getLength(); elementLength = dt.getLength();
} }
else if (modifier instanceof BitfieldSpecPiece) {
if (allowedTypes != AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD) {
throw new InvalidDataTypeException("bitfield not permitted");
}
if (destinationDataTypeManager == null) {
throw new AssertException(
"bitfields require destination datatype manager to be specified");
}
int bitSize = ((BitfieldSpecPiece) modifier).getBitSize();
dt = new ProxyBitFieldDataType(dt.clone(destinationDataTypeManager), bitSize);
}
} }
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
@ -375,7 +431,7 @@ public class DataTypeParser {
int nextIndex = 0; int nextIndex = 0;
while (nextIndex < dataTypeString.length()) { while (nextIndex < dataTypeString.length()) {
char c = dataTypeString.charAt(nextIndex); char c = dataTypeString.charAt(nextIndex);
if (c == '*' || c == '[' || c == '{') { if (c == '*' || c == '[' || c == ':' || c == '{') {
return dataTypeString.substring(0, nextIndex).trim(); return dataTypeString.substring(0, nextIndex).trim();
} }
++nextIndex; ++nextIndex;
@ -384,7 +440,7 @@ public class DataTypeParser {
} }
private static String[] splitDataTypeModifiers(String dataTypeModifiers) { private static String[] splitDataTypeModifiers(String dataTypeModifiers) {
dataTypeModifiers = dataTypeModifiers.replaceAll("[ \\t]", ""); dataTypeModifiers = dataTypeModifiers.replaceAll(":[ \\t]", "");
if (dataTypeModifiers.length() == 0) { if (dataTypeModifiers.length() == 0) {
return new String[0]; return new String[0];
} }
@ -393,7 +449,7 @@ public class DataTypeParser {
int nextIndex = 1; int nextIndex = 1;
while (nextIndex < dataTypeModifiers.length()) { while (nextIndex < dataTypeModifiers.length()) {
char c = dataTypeModifiers.charAt(nextIndex); char c = dataTypeModifiers.charAt(nextIndex);
if (c == '*' || c == '[' || c == '{') { if (c == '*' || c == '[' || c == ':' || c == '{') {
list.add(dataTypeModifiers.substring(startIndex, nextIndex)); list.add(dataTypeModifiers.substring(startIndex, nextIndex));
startIndex = nextIndex; startIndex = nextIndex;
} }
@ -420,7 +476,7 @@ public class DataTypeParser {
destinationDataTypeManager); destinationDataTypeManager);
} }
private static int parseArraySize(String numStr) { private static int parseSize(String numStr) {
numStr = (numStr == null ? "" : numStr.trim()); numStr = (numStr == null ? "" : numStr.trim());
if (numStr.length() == 0) { if (numStr.length() == 0) {
throw new NumberFormatException(); throw new NumberFormatException();
@ -435,6 +491,30 @@ public class DataTypeParser {
// dummy interface so we don't have to use Object in the list container // dummy interface so we don't have to use Object in the list container
} }
private static class BitfieldSpecPiece implements DtPiece {
int bitSize;
BitfieldSpecPiece(String piece) throws InvalidDataTypeException {
if (piece.startsWith(":")) {
String bitSizeStr = piece.substring(1);
try {
bitSize = parseSize(bitSizeStr);
if (bitSize >= 0) {
return;
}
}
catch (NumberFormatException e) {
// handled below
}
}
throw new InvalidDataTypeException("invalid bitfield specification: " + piece);
}
int getBitSize() {
return bitSize;
}
}
private static class ArraySpecPiece implements DtPiece { private static class ArraySpecPiece implements DtPiece {
int elementCount; int elementCount;
@ -442,7 +522,7 @@ public class DataTypeParser {
if (piece.startsWith("[") && piece.endsWith("]")) { if (piece.startsWith("[") && piece.endsWith("]")) {
String elementCountStr = piece.substring(1, piece.length() - 1); String elementCountStr = piece.substring(1, piece.length() - 1);
try { try {
elementCount = parseArraySize(elementCountStr); elementCount = parseSize(elementCountStr);
return; return;
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
@ -492,7 +572,7 @@ public class DataTypeParser {
if (piece.startsWith("{") && piece.endsWith("}")) { if (piece.startsWith("{") && piece.endsWith("}")) {
String elementSizeStr = piece.substring(1, piece.length() - 1); String elementSizeStr = piece.substring(1, piece.length() - 1);
try { try {
elementSize = parseArraySize(elementSizeStr); elementSize = parseSize(elementSizeStr);
return; return;
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {

View file

@ -72,6 +72,10 @@ public class ArrayDataType extends DataTypeImpl implements Array {
} }
private void validate(DataType dt) { private void validate(DataType dt) {
if (dt instanceof BitFieldDataType) {
throw new IllegalArgumentException(
"Array data-type may not be a bitfield: " + dt.getName());
}
if (dt instanceof FactoryDataType) { if (dt instanceof FactoryDataType) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Array data-type may not be a Factory data-type: " + dt.getName()); "Array data-type may not be a Factory data-type: " + dt.getName());

View file

@ -90,7 +90,7 @@ public class BitFieldDataType extends AbstractDataType {
/** /**
* Construct a bit-field type based upon a supported baseDataType. * Construct a bit-field type based upon a supported baseDataType.
* @param baseDataType a supported primitive integer data type or TypeDef to such a type. * @param baseDataType a supported primitive integer data type or TypeDef to such a type.
* A deep clone of this type will be performed using the specified dataMgr. * The baseType must already be cloned to the target datatype manager.
* @param bitSize size of bit-field expressed as number of bits * @param bitSize size of bit-field expressed as number of bits
* @throws InvalidDataTypeException if specified baseDataType is not permitted * @throws InvalidDataTypeException if specified baseDataType is not permitted
*/ */

View file

@ -112,6 +112,11 @@ public class PointerDataType extends BuiltIn implements Pointer {
public PointerDataType(DataType referencedDataType, int length, DataTypeManager dtm) { public PointerDataType(DataType referencedDataType, int length, DataTypeManager dtm) {
super(referencedDataType != null ? referencedDataType.getCategoryPath() : null, super(referencedDataType != null ? referencedDataType.getCategoryPath() : null,
constructUniqueName(referencedDataType, length), dtm); constructUniqueName(referencedDataType, length), dtm);
if (referencedDataType instanceof BitFieldDataType) {
throw new IllegalArgumentException(
"Pointer reference data-type may not be a bitfield: " +
referencedDataType.getName());
}
this.length = length <= 0 ? -1 : length; this.length = length <= 0 ? -1 : length;
this.referencedDataType = referencedDataType; this.referencedDataType = referencedDataType;
if (referencedDataType != null) { if (referencedDataType != null) {

View file

@ -75,6 +75,10 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
} }
private void validate(DataType dt) { private void validate(DataType dt) {
if (dt instanceof BitFieldDataType) {
throw new IllegalArgumentException(
"TypeDef data-type may not be a bitfield: " + dt.getName());
}
if (dt instanceof FactoryDataType) { if (dt instanceof FactoryDataType) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"TypeDef data-type may not be a Factory data-type: " + dt.getName()); "TypeDef data-type may not be a Factory data-type: " + dt.getName());