mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-28 23:21:46 +00:00
BitFields - added direct parse support of bitfield entry within
composite. Restrict use of bitfield datatype.
This commit is contained in:
parent
5ac462441a
commit
1fcad78bc4
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user