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()) {
throw new IllegalArgumentException("editOrdinal does not correspond to bitfield");
}
bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc), false);
bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc));
setApplyEnabled(true);
}

View File

@ -193,7 +193,8 @@ public class BitFieldEditorPanel extends JPanel {
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.setTabCommitsEdit(true);
//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.
* @param bitfieldDtc bitfield component or null
* @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,
boolean useCurrentAllocation) {
void initEdit(DataTypeComponent bitfieldDtc, int allocationOffset) {
String initialFieldName = null;
DataType initialBaseDataType = null;
int allocationSize = -1;
@ -387,9 +385,13 @@ public class BitFieldEditorPanel extends JPanel {
initialFieldName = bitfieldDtc.getFieldName();
BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType();
initialBaseDataType = bitfieldDt.getBaseDataType();
if (!useCurrentAllocation || allocationSize < 1) {
if (allocationSize < 1) {
allocationSize = initialBaseDataType.getLength();
}
int allocationAdjust = composite.getLength() - allocationOffset - allocationSize;
if (allocationAdjust < 0) {
allocationSize += allocationAdjust;
}
}
if (allocationSize < 1) {
allocationSize = 4;

View File

@ -1216,6 +1216,7 @@ public abstract class CompositeEditorPanel extends JPanel
private DataTypeSelectionEditor editor;
private DataType dt;
private int maxLength;
private boolean bitfieldAllowed;
private JPanel editorPanel;
@ -1224,6 +1225,7 @@ public abstract class CompositeEditorPanel extends JPanel
boolean isSelected, int row, int column) {
model.clearStatus();
maxLength = model.getMaxAddLength(row);
bitfieldAllowed = model.isBitFieldAllowed();
init();
DataTypeInstance dti = (DataTypeInstance) value;
@ -1243,7 +1245,9 @@ public abstract class CompositeEditorPanel extends JPanel
Plugin plugin = provider.getPlugin();
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);
DataTypeManager originalDataTypeManager = model.getOriginalDataTypeManager();
editor.setPreferredDataTypeManager(originalDataTypeManager);

View File

@ -87,6 +87,11 @@ public interface EditorModel {
*/
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.
*/

View File

@ -181,8 +181,7 @@ class StructureEditorModel extends CompEditorModel {
@Override
public DataTypeComponent getComponent(int rowIndex) {
int numComponents = getNumComponents();
if (rowIndex < 0 || rowIndex == numComponents) {
if (numComponents == 0 || rowIndex < 0 || rowIndex == numComponents) {
return null;
}
Structure viewStruct = (Structure) viewComposite;
@ -571,6 +570,11 @@ class StructureEditorModel extends CompEditorModel {
// 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
* 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.
// *************************************************************
@Override
public boolean isBitFieldAllowed() {
return isSingleRowSelection();
}
/**
* Returns whether or not the selection
* is allowed to be changed into an array.
*/
@Override
public boolean isArrayAllowed() {
return (getNumSelectedComponentRows() == 1);
return isSingleRowSelection();
}
/**

View File

@ -687,9 +687,14 @@ class StackEditorModel extends CompositeEditorModel {
return true;
}
@Override
public boolean isBitFieldAllowed() {
return false;
}
@Override
public boolean isArrayAllowed() {
if (getNumSelectedRows() != 1) {
if (getNumSelectedRows() != 1 || viewComposite == null) {
return false;
}
int index = getMinIndexSelected();
@ -1437,11 +1442,17 @@ class StackEditorModel extends CompositeEditorModel {
@Override
public DataTypeComponent getComponent(int rowIndex) {
if (viewComposite == null) {
return null;
}
return viewComposite.getComponent(rowIndex);
}
@Override
public int getNumComponents() {
if (viewComposite == null) {
return 0;
}
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 (!isValidDataType()) {
return promptUserToCreateDataType();
return parseDataTypeTextEntry();
}
return true;
@ -339,8 +339,12 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
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) {
// 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
DataType newDataType = null;
// try {
DataTypeParser parser =
new DataTypeParser(dataTypeManager, null, dataTypeManagerService, allowedDataTypes);
DataTypeParser parser = new DataTypeParser(dataTypeManager, dataTypeManager,
dataTypeManagerService, allowedDataTypes);
newDataType = parser.parse(selectionField.getText(), getDataTypeRootForCurrentText());
if (newDataType != null) {
if (maxSize >= 0 && newDataType.getLength() > newDataType.getLength()) {
@ -360,23 +363,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
selectionField.setSelectedValue(newDataType);
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;
}

View File

@ -23,6 +23,7 @@ import ghidra.app.services.DataTypeManagerService;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
public class DataTypeParser {
@ -39,6 +40,15 @@ public class DataTypeParser {
* All fixed-length data-types and sizable Dynamic(i.e., canSpecifyLength) data-types
*/
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
*/
@ -54,6 +64,26 @@ public class DataTypeParser {
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 destinationDataTypeManager; // may be null
private DataTypeManagerService dataTypeManagerService; // may be null
@ -162,6 +192,12 @@ public class DataTypeParser {
*/
public static void ensureIsAllowableType(DataType dt, AllowedDataTypes allowedTypes)
throws InvalidDataTypeException {
if (dt instanceof BitFieldDataType) {
if (allowedTypes != AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD) {
throw new InvalidDataTypeException("bitfield data-type not allowed");
}
return;
}
switch (allowedTypes) {
case DYNAMIC:
if (dt instanceof FactoryDataType) {
@ -169,6 +205,7 @@ public class DataTypeParser {
}
break;
case SIZABLE_DYNAMIC:
case SIZABLE_DYNAMIC_AND_BITFIELD:
if (dt instanceof FactoryDataType) {
throw new InvalidDataTypeException("factory data-type not allowed");
}
@ -205,7 +242,11 @@ public class DataTypeParser {
throws InvalidDataTypeException {
int arraySequenceStartIndex = -1;
List<DtPiece> modifiers = new ArrayList<>();
boolean terminalModifier = false;
for (String piece : splitDataTypeModifiers(dataTypeModifiers)) {
if (terminalModifier) {
throw new InvalidDataTypeException("Invalid data type modifier");
}
if (piece.startsWith("*")) {
modifiers.add(new PointerSpecPiece(piece));
arraySequenceStartIndex = -1;
@ -221,6 +262,10 @@ public class DataTypeParser {
modifiers.add(arraySpec);
}
}
else if (piece.startsWith(":")) {
terminalModifier = true;
modifiers.add(new BitfieldSpecPiece(piece));
}
else if (piece.startsWith("{")) {
// # indicates the size of an array element when the base data type is dynamic.
modifiers.add(new ElementSizeSpecPiece(piece));
@ -241,11 +286,22 @@ public class DataTypeParser {
elementLength = ((ElementSizeSpecPiece) modifier).getElementSize();
}
}
else {
else if (modifier instanceof ArraySpecPiece) {
int elementCount = ((ArraySpecPiece) modifier).getElementCount();
dt = createArrayDataType(dt, elementLength, elementCount);
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) {
@ -375,7 +431,7 @@ public class DataTypeParser {
int nextIndex = 0;
while (nextIndex < dataTypeString.length()) {
char c = dataTypeString.charAt(nextIndex);
if (c == '*' || c == '[' || c == '{') {
if (c == '*' || c == '[' || c == ':' || c == '{') {
return dataTypeString.substring(0, nextIndex).trim();
}
++nextIndex;
@ -384,7 +440,7 @@ public class DataTypeParser {
}
private static String[] splitDataTypeModifiers(String dataTypeModifiers) {
dataTypeModifiers = dataTypeModifiers.replaceAll("[ \\t]", "");
dataTypeModifiers = dataTypeModifiers.replaceAll(":[ \\t]", "");
if (dataTypeModifiers.length() == 0) {
return new String[0];
}
@ -393,7 +449,7 @@ public class DataTypeParser {
int nextIndex = 1;
while (nextIndex < dataTypeModifiers.length()) {
char c = dataTypeModifiers.charAt(nextIndex);
if (c == '*' || c == '[' || c == '{') {
if (c == '*' || c == '[' || c == ':' || c == '{') {
list.add(dataTypeModifiers.substring(startIndex, nextIndex));
startIndex = nextIndex;
}
@ -420,7 +476,7 @@ public class DataTypeParser {
destinationDataTypeManager);
}
private static int parseArraySize(String numStr) {
private static int parseSize(String numStr) {
numStr = (numStr == null ? "" : numStr.trim());
if (numStr.length() == 0) {
throw new NumberFormatException();
@ -435,6 +491,30 @@ public class DataTypeParser {
// 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 {
int elementCount;
@ -442,7 +522,7 @@ public class DataTypeParser {
if (piece.startsWith("[") && piece.endsWith("]")) {
String elementCountStr = piece.substring(1, piece.length() - 1);
try {
elementCount = parseArraySize(elementCountStr);
elementCount = parseSize(elementCountStr);
return;
}
catch (NumberFormatException e) {
@ -492,7 +572,7 @@ public class DataTypeParser {
if (piece.startsWith("{") && piece.endsWith("}")) {
String elementSizeStr = piece.substring(1, piece.length() - 1);
try {
elementSize = parseArraySize(elementSizeStr);
elementSize = parseSize(elementSizeStr);
return;
}
catch (NumberFormatException e) {

View File

@ -72,6 +72,10 @@ public class ArrayDataType extends DataTypeImpl implements Array {
}
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) {
throw new IllegalArgumentException(
"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.
* @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
* @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) {
super(referencedDataType != null ? referencedDataType.getCategoryPath() : null,
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.referencedDataType = referencedDataType;
if (referencedDataType != null) {

View File

@ -75,6 +75,10 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
}
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) {
throw new IllegalArgumentException(
"TypeDef data-type may not be a Factory data-type: " + dt.getName());