diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index ed2497532a..74a32e5887 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -921,11 +921,13 @@ src/main/resources/images/L.gif||GHIDRA||||END| src/main/resources/images/LocalVariable.gif||GHIDRA||||END| src/main/resources/images/M.gif||GHIDRA||||END| src/main/resources/images/Merge.png||GHIDRA||||END| +src/main/resources/images/Minus.png||GHIDRA||||END| src/main/resources/images/MultiDuplicateData.png||GHIDRA||||END| src/main/resources/images/Namespace.gif||GHIDRA||||END| src/main/resources/images/NextHighlightBlock16.gif||GHIDRA||||END| src/main/resources/images/NextSelectionBlock16.gif||GHIDRA||||END| src/main/resources/images/Parameter.gif||GHIDRA||||END| +src/main/resources/images/Plus.png||GHIDRA||||END| src/main/resources/images/PreviousHighlightBlock16.gif||GHIDRA||||END| src/main/resources/images/PreviousSelectionBlock16.gif||GHIDRA||||END| src/main/resources/images/Program.gif||GHIDRA||||END| diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm index 6eda43e18e..bca14ff026 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm @@ -163,6 +163,8 @@ be specified.

+ +

Unaligned Structures

@@ -301,7 +303,9 @@
- + + +

Flexible Array Component

A structure may be defined with a trailing flexible array component which corresponds to @@ -321,7 +325,17 @@ be reflected in decompilation results. Its primary purpose if to reflect the C source definition of a structure with correct alignment and structure sizing.

-
+

Note: The equivalent of having no C #pragma pack attribute on the structure or union is to + choose none. The equivalent for a C code attribute of + #pragma pack() without a value is to specify a pack value of 1. + The equivalent of # pragma pack(4) is to specify a pack + value of 4.
+

+ +


@@ -334,6 +348,8 @@ table. There are also short-cut keys associated with each of the edit actions.

+
+

Insert Undefined Byte

@@ -557,6 +573,7 @@ is created and a component containing it replaces the selected components.


+

Component Fields

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java index 3c9677a69e..6a086b0e49 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DefaultDataTypeManagerService.java @@ -23,16 +23,17 @@ import java.util.Map.Entry; import javax.swing.tree.TreePath; import generic.jar.ResourceFile; -import ghidra.app.plugin.core.datamgr.archive.Archive; -import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; +import ghidra.app.plugin.core.datamgr.archive.*; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; +import ghidra.app.plugin.core.datamgr.util.DataTypeComparator; import ghidra.app.services.DataTypeManagerService; import ghidra.program.model.data.*; import ghidra.program.model.listing.DataTypeArchive; import ghidra.util.HelpLocation; import ghidra.util.UniversalID; -class DefaultDataTypeManagerService implements DataTypeManagerService { +// FIXME!! TESTING +public class DefaultDataTypeManagerService implements DataTypeManagerService { private Map archiveMap = new HashMap<>(); private DataTypeManager builtInDataTypesManager = BuiltInDataTypeManager.getDataTypeManager(); @@ -156,6 +157,9 @@ class DefaultDataTypeManagerService implements DataTypeManagerService { @Override public DataType getDataType(TreePath selectedTreeNode) { + if (selectedTreeNode == null) { + return null; + } throw new UnsupportedOperationException(); } @@ -171,7 +175,11 @@ class DefaultDataTypeManagerService implements DataTypeManagerService { @Override public List getSortedDataTypeList() { - throw new UnsupportedOperationException(); + List dataTypes = + builtInDataTypesManager.getDataTypes(BuiltInSourceArchive.INSTANCE); + dataTypes.sort(new DataTypeComparator()); + return dataTypes; +// throw new UnsupportedOperationException(); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java new file mode 100644 index 0000000000..9ca932a613 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java @@ -0,0 +1,112 @@ +/* ### + * 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.compositeeditor; + +import java.awt.Component; +import java.awt.Window; + +import javax.swing.JTable; +import javax.swing.SwingUtilities; + +import docking.ActionContext; +import docking.DockingWindowManager; +import ghidra.program.model.data.*; +import ghidra.util.exception.AssertException; + +/** + * Action for use in the composite data type editor. + * This action has help associated with it. + */ +public class AddBitFieldAction extends CompositeEditorTableAction { + + private final static String ACTION_NAME = "Add Bitfield"; + private final static String GROUP_NAME = BITFIELD_ACTION_GROUP; + private final static String DESCRIPTION = + "Add a bitfield at the position of a selected component"; + private static String[] popupPath = new String[] { ACTION_NAME }; + + public AddBitFieldAction(CompositeEditorProvider provider) { + super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null); + setDescription(DESCRIPTION); + if (!(model instanceof CompEditorModel)) { + throw new AssertException("unsupported use"); + } + adjustEnablement(); + } + + @Override + public void actionPerformed(ActionContext context) { + + CompEditorModel editorModel = (CompEditorModel) model; + if (editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) { + return; + } + int rowIndex = model.getSelectedRows()[0]; + + if (editorModel.isAligned()) { + // Insert before selected component + // ordinal based, user input needed: + // 1. bitfield base datatype + // 2. bitfield size + // 3. bitfield name (can be renamed later) + int ordinal = -1; + DataType baseDataType = null; + if (!editorModel.isAtEnd(rowIndex)) { + DataTypeComponent component = editorModel.getComponent(rowIndex); + ordinal = component.getOrdinal(); + if (component.isBitFieldComponent()) { + BitFieldDataType currentBitfield = (BitFieldDataType) component.getDataType(); + baseDataType = currentBitfield.getBaseDataType(); + } + } + insertBitField(ordinal, baseDataType); + } + else { + BitFieldEditorDialog dlg = + new BitFieldEditorDialog(editorModel.viewComposite, provider.dtmService, + -(rowIndex + 1), ordinal -> refreshTableAndSelection(editorModel, ordinal)); + Component c = provider.getComponent(); + Window w = SwingUtilities.windowForComponent(c); + DockingWindowManager.showDialog(w, dlg, c); + } + + requestTableFocus(); + } + + private void refreshTableAndSelection(CompEditorModel editorModel, int ordinal) { + editorModel.fireTableDataChanged(); + editorModel.compositeInfoChanged(); + JTable editorTable = provider.getTable(); + editorTable.getSelectionModel().setSelectionInterval(ordinal, ordinal); + } + + private void insertBitField(int ordinal, DataType baseDataType) { + // TODO Auto-generated method stub + + } + + @Override + public void adjustEnablement() { + boolean enabled = true; + CompEditorModel editorModel = (CompEditorModel) model; + if (editorModel.viewComposite == null || editorModel.getNumSelectedRows() != 1 || + editorModel.isFlexibleArraySelection()) { + enabled = false; + } + setEnabled(enabled); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java new file mode 100644 index 0000000000..d96533c300 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java @@ -0,0 +1,302 @@ +/* ### + * 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.compositeeditor; + +import java.awt.event.MouseEvent; + +import javax.swing.Icon; +import javax.swing.JComponent; + +import docking.*; +import docking.action.DockingAction; +import docking.action.MenuData; +import ghidra.GhidraApplicationLayout; +import ghidra.app.plugin.core.analysis.DefaultDataTypeManagerService; +import ghidra.app.services.DataTypeManagerService; +import ghidra.framework.*; +import ghidra.program.model.data.*; +import ghidra.util.SystemUtilities; +import resources.ResourceManager; + +public class BitFieldEditorDialog extends DialogComponentProvider { + + private static final Icon ADD_ICON = ResourceManager.loadImage("images/Plus.png"); + private static final Icon EDIT_ICON = ResourceManager.loadImage("images/move.png"); + private static final Icon DELETE_ICON = ResourceManager.loadImage("images/edit-delete.png"); + + private DataTypeManagerService dtmService; + private Composite composite; + private CompositeChangeListener listener; + + private BitFieldEditorPanel bitFieldEditorPanel; // for unaligned use case + + BitFieldEditorDialog(Composite composite, DataTypeManagerService dtmService, int editOrdinal, + CompositeChangeListener listener) { + super("Edit " + getCompositeType(composite) + " Bitfield"); + this.composite = composite; + this.listener = listener; + this.dtmService = dtmService; + addButtons(); + addWorkPanel(buildWorkPanel(editOrdinal)); + setRememberLocation(false); + setRememberSize(false); + + addActions(); + } + + private void addButtons() { + addOKButton(); + addCancelButton(); + if (composite instanceof Structure) { + addApplyButton(); + setApplyEnabled(false); + } + } + + private static DataTypeComponent getEditComponent(ActionContext context, boolean bitFieldOnly) { + if (!(context instanceof BitFieldEditorPanel.BitFieldEditorContext)) { + return null; + } + BitFieldEditorPanel.BitFieldEditorContext editorContext = + (BitFieldEditorPanel.BitFieldEditorContext) context; + DataTypeComponent dtc = editorContext.getSelectedComponent(); + if (dtc != null && (!bitFieldOnly || dtc.isBitFieldComponent())) { + return dtc; + } + return null; + } + + private class EditBitFieldAction extends DockingAction { + + EditBitFieldAction() { + super("Edit Bitfield", "BitFieldEditorDialog"); + setPopupMenuData(new MenuData(new String[] { getName() }, EDIT_ICON)); + + } + + @Override + public void actionPerformed(ActionContext context) { + DataTypeComponent bitfieldDtc = getEditComponent(context, true); + if (bitfieldDtc == null || !bitFieldEditorPanel.endCurrentEdit()) { + return; + } + initEdit(bitfieldDtc.getOrdinal()); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return getEditComponent(context, true) != null; + } + } + + private class AddBitFieldAction extends DockingAction { + + AddBitFieldAction() { + super("Add Bitfield", "BitFieldEditorDialog"); + setPopupMenuData(new MenuData(new String[] { getName() }, ADD_ICON)); + } + + @Override + public void actionPerformed(ActionContext context) { + if (!bitFieldEditorPanel.endCurrentEdit()) { + return; + } + BitFieldEditorPanel.BitFieldEditorContext editorContext = + (BitFieldEditorPanel.BitFieldEditorContext) context; + + bitFieldEditorPanel.initAdd(null, editorContext.getAllocationOffset(), + editorContext.getSelectedBitOffset(), true); + setApplyEnabled(true); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return (context instanceof BitFieldEditorPanel.BitFieldEditorContext) && + !bitFieldEditorPanel.isAdding(); + } + } + + private class DeleteComponentAction extends DockingAction { + + DeleteComponentAction() { + super("Delete Component", "BitFieldEditorDialog"); + setPopupMenuData(new MenuData(new String[] { getName() }, DELETE_ICON)); + } + + @Override + public void actionPerformed(ActionContext context) { + DataTypeComponent bitfieldDtc = getEditComponent(context, false); + if (bitfieldDtc == null) { + return; + } + int ordinal = bitfieldDtc.getOrdinal(); + composite.delete(ordinal); + bitFieldEditorPanel.componentDeleted(ordinal); + if (listener != null) { + listener.componentChanged(ordinal); + } + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return getEditComponent(context, false) != null; + } + } + + private void addActions() { + addAction(new AddBitFieldAction()); + addAction(new EditBitFieldAction()); + addAction(new DeleteComponentAction()); + } + + @Override + protected void applyCallback() { + if (bitFieldEditorPanel.isEditing() && bitFieldEditorPanel.apply(listener)) { + setApplyEnabled(false); + } + } + + @Override + protected void okCallback() { + applyCallback(); + close(); + } + + @Override + public ActionContext getActionContext(MouseEvent event) { + ActionContext context = bitFieldEditorPanel.getActionContext(event); + if (context != null) { + return context; + } + return super.getActionContext(event); + } + + @Override + protected void cancelCallback() { + // TODO: Should we cancel without asking? + if (!bitFieldEditorPanel.endCurrentEdit()) { + return; + } + super.cancelCallback(); + } + + private JComponent buildWorkPanel(int editOrdinal) { + bitFieldEditorPanel = new BitFieldEditorPanel(composite, dtmService); + if (editOrdinal < 0) { + initAdd(-editOrdinal - 1); + } + else { + initEdit(editOrdinal); + } + return bitFieldEditorPanel; + } + + private static String getCompositeType(Composite composite) { + // currently supports unaligned case only! + if (composite.isInternallyAligned()) { + throw new IllegalArgumentException("Aligned use not supported"); + } + String alignmentMode = composite.isInternallyAligned() ? "Aligned" : "Unaligned"; + String type = (composite instanceof Union) ? "Union" : "Structure"; + return alignmentMode + " " + type; + } + + private void initAdd(int ordinal) { + DataType baseDataType = null; + int offset = 0; + if (ordinal < composite.getNumComponents()) { + DataTypeComponent dtc = composite.getComponent(ordinal); + offset = dtc.getOffset(); + if (dtc.isBitFieldComponent()) { + baseDataType = ((BitFieldDataType) dtc.getDataType()).getBaseDataType(); + } + } + else if (!composite.isNotYetDefined()) { + offset = composite.getLength(); + } + + // use previous or default base datatype + bitFieldEditorPanel.initAdd(baseDataType, offset, 0, false); + setApplyEnabled(true); + } + + private void initEdit(int editOrdinal) throws ArrayIndexOutOfBoundsException { + DataTypeComponent dtc = composite.getComponent(editOrdinal); + if (!dtc.isBitFieldComponent()) { + throw new IllegalArgumentException("editOrdinal does not correspond to bitfield"); + } + bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc), false); + setApplyEnabled(true); + } + + private int getPreferredAllocationOffset(DataTypeComponent bitfieldDtc) { + if (composite instanceof Union) { + return 0; + } + + BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType(); + int offset = bitfieldDtc.getOffset(); + int baseTypeSize = bitfieldDt.getBaseTypeSize(); + if (bitfieldDtc.getLength() >= baseTypeSize) { + return offset; // do not adjust + } + + DataOrganization dataOrganization = composite.getDataOrganization(); + + // Assume a reasonable alignment in identifying aligned offset + int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, + Composite.NOT_PACKING, bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize()); + + int adjustedOffset = offset - (offset % alignment); + + // only adjust if bitfield fits within aligned offset + if (bitfieldDtc.getEndOffset() <= (adjustedOffset + baseTypeSize - 1)) { + return adjustedOffset; + } + + return offset; + } + + public static void main(String[] args) throws Exception { + + //UniversalIdGenerator.initialize(); + ApplicationConfiguration configuration = new HeadlessGhidraApplicationConfiguration(); + configuration.setInitializeLogging(false); + Application.initializeApplication(new GhidraApplicationLayout(), configuration); + + Structure s = new StructureDataType("Foo", 0); + DataTypeComponent dtcA = + s.insertBitFieldAt(0, 4, 16, IntegerDataType.dataType, 4, "BitA", null); + DataTypeComponent dtcZ = + s.insertBitFieldAt(0, 4, 16, IntegerDataType.dataType, 0, "BitZ", null); + DataTypeComponent dtcB = + s.insertBitFieldAt(0, 4, 12, IntegerDataType.dataType, 4, "BitB", null); + DataTypeComponent dtcC = + s.insertBitFieldAt(0, 4, 4, IntegerDataType.dataType, 4, "BitC", null); + + DockingWindowManager winMgr = new DockingWindowManager("TEST", null, null); + + BitFieldEditorDialog dlg = + new BitFieldEditorDialog(s, new DefaultDataTypeManagerService(), -1, null); + + SystemUtilities.runSwingNow(() -> { + winMgr.setVisible(true); + DockingWindowManager.showDialog(null, dlg); + }); + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java new file mode 100644 index 0000000000..743fa360d0 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java @@ -0,0 +1,581 @@ +/* ### + * 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.compositeeditor; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; + +import docking.ActionContext; +import docking.widgets.DropDownSelectionTextField; +import docking.widgets.OptionDialog; +import ghidra.app.plugin.core.compositeeditor.BitFieldPlacementComponent.BitAttributes; +import ghidra.app.plugin.core.compositeeditor.BitFieldPlacementComponent.BitFieldAllocation; +import ghidra.app.services.DataTypeManagerService; +import ghidra.app.util.datatype.DataTypeSelectionEditor; +import ghidra.app.util.datatype.NavigationDirection; +import ghidra.program.model.data.*; +import ghidra.util.Msg; +import ghidra.util.data.DataTypeParser.AllowedDataTypes; +import ghidra.util.layout.*; +import resources.ResourceManager; + +/** + * BitFieldEditorPanel provides the ability to place bitfields + * within unaligned structures and unions. + */ +public class BitFieldEditorPanel extends JPanel { + + private static final Icon DECREMENT_ICON = ResourceManager.loadImage("images/Minus.png"); + private static final Icon INCREMENT_ICON = ResourceManager.loadImage("images/Plus.png"); + + private static final String ENTRY_ERROR_DIALOG_TITLE = "Bitfield Entry Error"; + + private DataTypeManagerService dtmService; + private Composite composite; + + private JLabel allocationOffsetLabel; + JButton decrementButton; + JButton incrementButton; + + private BitFieldPlacementComponent placementComponent; + private DataType baseDataType; + + private DataTypeSelectionEditor dtChoiceEditor; + private JTextField fieldNameTextField; + private SpinnerNumberModel allocSizeModel; + private JSpinnerWithMouseWheel allocSizeInput; + private SpinnerNumberModel bitOffsetModel; + private JSpinnerWithMouseWheel bitOffsetInput; + private SpinnerNumberModel bitSizeModel; + private JSpinnerWithMouseWheel bitSizeInput; + + private boolean updating = false; + + BitFieldEditorPanel(Composite composite, DataTypeManagerService dtmService) { + super(); + this.composite = composite; + + if (composite.isInternallyAligned()) { + // A different bitfield editor should be used for aligned composites + throw new IllegalArgumentException("composite must be unaligned"); + } + + setLayout(new VerticalLayout(5)); + setFocusTraversalKeysEnabled(true); + + this.dtmService = dtmService; + + setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + createPlacementPanel(); + + if (composite instanceof Structure) { + add(createAllocationOffsetPanel()); + } + add(placementComponent); + add(createEntryPanel()); + + enableControls(false); + } + + private JPanel createAllocationOffsetPanel() { + + JPanel panel = new JPanel(new HorizontalLayout(5)); + + decrementButton = new JButton(DECREMENT_ICON); + decrementButton.setFocusable(false); + decrementButton.setToolTipText("Decrement allocation unit offset"); + decrementButton.addActionListener(e -> adjustAllocationOffset(-1)); + panel.add(decrementButton); + + incrementButton = new JButton(INCREMENT_ICON); + incrementButton.setFocusable(false); + incrementButton.setToolTipText("Increment allocation unit offset"); + incrementButton.addActionListener(e -> adjustAllocationOffset(1)); + panel.add(incrementButton); + + allocationOffsetLabel = new JLabel(); + allocationOffsetLabel.setHorizontalTextPosition(SwingConstants.LEFT); + panel.add(allocationOffsetLabel); + + return panel; + } + + private void adjustAllocationOffset(int delta) { + int adjustedOffset = placementComponent.getAllocationOffset() + delta; + if (adjustedOffset < 0 || adjustedOffset > composite.getLength()) { + return; + } + placementComponent.setAllocationOffset(adjustedOffset); + updateAllocationOffsetLabel(); + } + + private void updateAllocationOffsetLabel() { + if (composite instanceof Structure) { + String text = + "Structure Offset of Allocation Unit: " + placementComponent.getAllocationOffset(); + allocationOffsetLabel.setText(text); + + int offset = placementComponent.getAllocationOffset(); + decrementButton.setEnabled(offset > 0); + int length = composite.isNotYetDefined() ? 0 : composite.getLength(); + incrementButton.setEnabled(offset < length); + } + } + + private JPanel createEntryPanel() { + + JComponent baseDataTypeEditor = createDataTypeChoiceEditor(); + + fieldNameTextField = new JTextField(20); + fieldNameTextField.setFocusable(true); + + allocSizeModel = new SpinnerNumberModel(Long.valueOf(4), Long.valueOf(1), Long.valueOf(16), + Long.valueOf(1)); + allocSizeInput = new JSpinnerWithMouseWheel(allocSizeModel); + + bitOffsetModel = new SpinnerNumberModel(Long.valueOf(0), Long.valueOf(0), Long.valueOf(31), + Long.valueOf(1)); + bitOffsetInput = new JSpinnerWithMouseWheel(bitOffsetModel); + + bitSizeModel = new SpinnerNumberModel(Long.valueOf(4), Long.valueOf(0), Long.valueOf(4 * 8), + Long.valueOf(1)); + bitSizeInput = new JSpinnerWithMouseWheel(bitSizeModel); + + allocSizeModel.addChangeListener(e -> update()); + bitSizeModel.addChangeListener(e -> update()); + bitOffsetModel.addChangeListener(e -> update()); + + JPanel entryPanel = new JPanel(new TwoColumnPairLayout(5, 15, 5, 0)); + entryPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), + BorderFactory.createEmptyBorder(5, 5, 5, 5))); + entryPanel.setFocusCycleRoot(true); + + entryPanel.add(new JLabel("Base Datatype:")); + entryPanel.add(baseDataTypeEditor); + + entryPanel.add(new JLabel("Allocation Bytes:")); + entryPanel.add(allocSizeInput); + + entryPanel.add(new JLabel("Field Name:")); + entryPanel.add(fieldNameTextField); + + entryPanel.add(new JLabel("Bit Size:")); + entryPanel.add(bitSizeInput); + + entryPanel.add(new JPanel()); + entryPanel.add(new JPanel()); + + entryPanel.add(new JLabel("Bit Offset:")); + entryPanel.add(bitOffsetInput); + return entryPanel; + } + + private JComponent createDataTypeChoiceEditor() { + + dtChoiceEditor = new DataTypeSelectionEditor(dtmService, -1, AllowedDataTypes.BITFIELD_USE); + dtChoiceEditor.setConsumeEnterKeyPress(false); + dtChoiceEditor.setTabCommitsEdit(true); + //dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager()); + + final DropDownSelectionTextField dtChoiceTextField = + dtChoiceEditor.getDropDownTextField(); + dtChoiceTextField.setBorder(UIManager.getBorder("TextField.border")); + + dtChoiceEditor.addCellEditorListener(new CellEditorListener() { + @Override + public void editingCanceled(ChangeEvent e) { + dtChoiceEditor.setCellEditorValue(baseDataType); // restore + } + + @Override + public void editingStopped(ChangeEvent e) { + if (!checkValidBaseDataType()) { + dtChoiceTextField.selectAll(); + } + else { + baseDataType = dtChoiceEditor.getCellEditorValueAsDataType(); + if (baseDataType != null) { + baseDataType = baseDataType.clone(composite.getDataTypeManager()); + } + updateBitSizeModel(); + NavigationDirection direction = dtChoiceEditor.getNavigationDirection(); + if (direction == NavigationDirection.FORWARD) { + allocSizeInput.requestFocus(); + } + else if (direction == NavigationDirection.BACKWARD) { + bitOffsetInput.requestFocus(); + } + } + } + }); + + dtChoiceEditor.getBrowseButton().setFocusable(false); + + JComponent editorComponent = dtChoiceEditor.getEditorComponent(); + Dimension preferredSize = editorComponent.getPreferredSize(); + editorComponent.setPreferredSize(new Dimension(200, preferredSize.height)); + return editorComponent; + } + + private JPanel createPlacementPanel() { + JPanel midPanel = new JPanel(new PairLayout(5, 5)); + + JPanel leftMidPanel = new JPanel(new VerticalLayout(13)); + leftMidPanel.setBorder(BorderFactory.createEmptyBorder(12, 8, 12, 0)); + JLabel byteOffsetLabel = new JLabel("Byte Offset:", SwingConstants.RIGHT); + byteOffsetLabel.setToolTipText("Byte Offset is relative to start of allocation unit"); + leftMidPanel.add(byteOffsetLabel); + leftMidPanel.add(new JLabel("Bits:", SwingConstants.RIGHT)); + midPanel.add(leftMidPanel); + + placementComponent = new BitFieldPlacementComponent(composite); + placementComponent.setFont(UIManager.getFont("TextField.font")); + placementComponent.addMouseWheelListener(e -> bitSizeInput.mouseWheelMoved(e)); + + placementComponent.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1 && + bitOffsetInput.isEnabled()) { + setBitFieldOffset(e.getPoint()); + } +// if (e.getClickCount() == 2 && endCurrentEdit() && +// editBitFieldComponent(e.getPoint())) { +// enableControls(true); +// } + } +// public void mousePressed(MouseEvent e) { +// if (e.isPopupTrigger()) { +// setBitFieldPopupContext(e.getPoint()); +// } +// }; + }); + + JScrollPane scrollPane = + new JScrollPane(placementComponent, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.getViewport().setBackground(getBackground()); + + midPanel.add(scrollPane); + return midPanel; + } + + private boolean checkValidBaseDataType() { + DropDownSelectionTextField textField = dtChoiceEditor.getDropDownTextField(); + String dtName = textField.getText().trim(); + try { + if (dtName.length() == 0 || !dtChoiceEditor.validateUserSelection()) { + Msg.showError(BitFieldEditorPanel.class, textField, ENTRY_ERROR_DIALOG_TITLE, + "Valid bitfield base datatype entry required"); + return false; + } + } + catch (InvalidDataTypeException e) { + Msg.showError(BitFieldEditorPanel.class, textField, ENTRY_ERROR_DIALOG_TITLE, + "Invalid bitfield base datatype: " + e.getMessage()); + return false; + } + + return true; + } + + void initAdd(DataType initialBaseDataType, int allocationOffset, int bitOffset, + boolean useCurrentAllocation) { + if (initialBaseDataType == null) { + initialBaseDataType = baseDataType; + } + if (!BitFieldDataType.isValidBaseDataType(initialBaseDataType)) { + initialBaseDataType = IntegerDataType.dataType.clone(composite.getDataTypeManager()); + } + placementComponent.setAllocationOffset(allocationOffset); + long allocationSize = useCurrentAllocation ? (Long) allocSizeModel.getValue() + : initialBaseDataType.getLength(); + placementComponent.initAdd((int) allocationSize, 1, bitOffset); + initControls(null, initialBaseDataType); + enableControls(true); + } + + /** + * Initialize for edit of existing component or no component if bitfieldDtc is null. + * 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) { + String initialFieldName = null; + DataType initialBaseDataType = null; + int allocationSize = -1; + BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); + if (bitFieldAllocation != null) { + allocationSize = bitFieldAllocation.getAllocationByteSize(); + } + if (bitfieldDtc != null) { + if (!bitfieldDtc.isBitFieldComponent()) { + throw new IllegalArgumentException("unsupport data type component"); + } + initialFieldName = bitfieldDtc.getFieldName(); + BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType(); + initialBaseDataType = bitfieldDt.getBaseDataType(); + if (!useCurrentAllocation || allocationSize < 1) { + allocationSize = initialBaseDataType.getLength(); + } + } + if (allocationSize < 1) { + allocationSize = 4; + } + // TODO: adjust offset and allocationSize if needed + placementComponent.setAllocationOffset(allocationOffset); + placementComponent.init(allocationSize, bitfieldDtc); + initControls(initialFieldName, initialBaseDataType); + enableControls(bitfieldDtc != null); + } + + void componentDeleted(int ordinal) { + placementComponent.componentDeleted(ordinal); + } + + private void initControls(String initialFieldName, DataType initialBaseDataType) { + updating = true; + try { + baseDataType = initialBaseDataType; + dtChoiceEditor.setCellEditorValue(initialBaseDataType); + fieldNameTextField.setText(initialFieldName); + + // Use current placementComponent to obtain initial values + BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); + allocSizeModel.setValue((long) bitFieldAllocation.getAllocationByteSize()); + int allocBits = 8 * bitFieldAllocation.getAllocationByteSize(); + bitSizeModel.setValue(1L); + bitOffsetModel.setMaximum((long) allocBits - 1); + bitOffsetModel.setValue((long) bitFieldAllocation.getBitOffset()); + updateBitSizeModel(); + + updateAllocationOffsetLabel(); + } + finally { + updating = false; + } + } + + /** + * @return true if actively editing or adding a bitfield + */ + boolean isEditing() { + return placementComponent.isEditing(); + } + + /** + * @return true if actively adding a bitfield + */ + boolean isAdding() { + return placementComponent.isAdding(); + } + + boolean endCurrentEdit() { + if (placementComponent.isEditing()) { + String currentOp = placementComponent.isAdding() ? "add" : "edit"; + int option = OptionDialog.showYesNoDialog(this, "Confirm Edit Action", + "Cancel current bitfield " + currentOp + " operation?"); + if (option != OptionDialog.YES_OPTION) { + return false; + } + placementComponent.cancelEdit(); + enableControls(false); + } + return true; + } + + boolean apply(CompositeChangeListener listener) { + boolean deleteConflicts = false; + if (placementComponent.hasApplyConflict()) { + long allocationSize = (Long) allocSizeModel.getValue(); + int option = OptionDialog.showOptionDialog(this, "Bitfield Conflict(s)", + "Bitfield placement conflicts with one or more components.\n" + + "Would you like to delete conflicts or move conflicts by " + allocationSize + + " bytes?", + "Delete Conflicts", "Move Conflicts", OptionDialog.WARNING_MESSAGE); + if (option == OptionDialog.CANCEL_OPTION) { + return false; + } + deleteConflicts = (option == OptionDialog.OPTION_ONE); + } + placementComponent.applyBitField(baseDataType, fieldNameTextField.getText().trim(), + deleteConflicts, listener); + enableControls(false); + return true; + } + + private void enableControls(boolean enable) { + allocSizeInput.setEnabled(enable); + bitSizeInput.setEnabled(enable); + bitOffsetInput.setEnabled(enable); + if (!enable) { + // TODO: set placementComponent mode to NONE + bitOffsetModel.setValue(0L); + bitSizeModel.setValue(1L); + fieldNameTextField.setText(null); + } + } + + private void setBitFieldOffset(Point point) { + int bitOffset = placementComponent.getBitOffset(point); + if (bitOffset >= 0) { + // long cast is required for auto-box to Long object + bitOffsetModel.setValue((long) bitOffset); + } + } + + private DataTypeComponent getDataTypeComponent(Point p) { + BitAttributes attrs = placementComponent.getBitAttributes(p); + if (attrs != null) { + return attrs.getDataTypeComponent(true); + } + return null; + } + + private void updateBitSizeModel() { + int allocSize = allocSizeModel.getNumber().intValue(); + int allocBits = 8 * allocSize; + int baseTypeBits = baseDataType != null ? (8 * baseDataType.getLength()) : allocBits; + long maxBitSize = Math.min(allocBits, baseTypeBits); + bitSizeModel.setMaximum(maxBitSize); + if (maxBitSize < (Long) bitSizeModel.getValue()) { + bitSizeModel.setValue(maxBitSize); + } + } + + private void update() { + if (updating) { + return; + } + updating = true; + try { + int allocSize = allocSizeModel.getNumber().intValue(); + int allocBits = 8 * allocSize; + updateBitSizeModel(); + bitOffsetModel.setMaximum(Long.valueOf(allocBits - 1)); + int bitSize = bitSizeModel.getNumber().intValue(); + + int boff = bitOffsetModel.getNumber().intValue(); + int total = bitSize + boff; + if (total > allocBits) { + boff -= total - allocBits; + if (boff < 0) { + boff = 0; + } + } + if (bitSize == 0) { + // force preferred placement of zero-length bit-field + // little-endian: lsb of byte + // big-endian: msb of byte + boff = 8 * (boff / 8); + if (placementComponent.isBigEndian()) { + boff += 7; + } + bitOffsetModel.setStepSize((long) 8); + } + else { + bitOffsetModel.setStepSize((long) 1); + } + bitOffsetModel.setValue(Long.valueOf(boff)); + if (bitSize > allocBits) { + bitSize = allocBits; + bitSizeModel.setValue(Long.valueOf(bitSize)); + } + placementComponent.refresh(allocSize, bitSize, boff); + } + finally { + updating = false; + } + } + + ActionContext getActionContext(MouseEvent event) { + if (placementComponent == event.getSource()) { + Point p = event.getPoint(); + return new BitFieldEditorContext(getDataTypeComponent(p), + placementComponent.getBitOffset(p)); + } + return null; + } + + class BitFieldEditorContext extends ActionContext { + + private int selectedBitOffset; + private DataTypeComponent selectedDtc; + + private BitFieldEditorContext(DataTypeComponent selectedDtc, int selectedBitOffset) { + this.selectedDtc = selectedDtc; + this.selectedBitOffset = selectedBitOffset; + } + + DataTypeComponent getSelectedComponent() { + return selectedDtc; + } + + public int getAllocationOffset() { + return placementComponent.getAllocationOffset(); + } + + public int getSelectedBitOffset() { + return selectedBitOffset; + } + + } + + private static class JSpinnerWithMouseWheel extends JSpinner implements MouseWheelListener { + + JSpinnerWithMouseWheel(SpinnerNumberModel model) { + super(model); + addMouseWheelListener(this); + } + + @Override + public void requestFocus() { + DefaultEditor editor = (DefaultEditor) getEditor(); + editor.getTextField().requestFocus(); + } + + @Override + public void mouseWheelMoved(MouseWheelEvent mwe) { + if (!isEnabled()) { + return; + } + if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) { + return; + } + SpinnerNumberModel m = (SpinnerNumberModel) getModel(); + if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) { + // TODO: Handle other mouse wheel modes + return; + } + Long value = + mwe.getUnitsToScroll() > 0 ? (Long) m.getPreviousValue() : (Long) m.getNextValue(); + if (value != null) { + setValue(value); + mwe.consume(); + } + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java new file mode 100644 index 0000000000..10bad8a058 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java @@ -0,0 +1,746 @@ +/* ### + * 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.compositeeditor; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.Arrays; +import java.util.HashSet; + +import javax.swing.JPanel; +import javax.swing.ToolTipManager; + +import ghidra.program.model.data.*; +import ghidra.program.model.data.Composite; +import ghidra.util.Msg; +import ghidra.util.exception.AssertException; + +public class BitFieldPlacementComponent extends JPanel { + + private static final int CELL_HEIGHT = 30; + private static final int BIT_WIDTH = 10; + private static final int ZERO_BIT_WIDTH = 3; + private static final int BIT_SEPARATOR_THICKNESS = 1; + private static final int BYTE_SEPARATOR_THICKNESS = 2; + private static final int BYTE_WIDTH = 8 * (BIT_WIDTH + BIT_SEPARATOR_THICKNESS); + private static final int SCROLLBAR_THICKNESS = 10; + private static final int MY_HEIGHT = (2 * CELL_HEIGHT) + (3 * BYTE_SEPARATOR_THICKNESS); + + private static final Color TEXT_COLOR = Color.black; + private static final Color LINE_COLOR = Color.black; + private static final Color BYTE_HEADER_COLOR = new Color(0xdfdfdf); + private static final Color UNDEFINED_BIT_COLOR = new Color(0xe8e8e8); + private static final Color BITFIELD_BITS_COLOR = Color.green; + private static final Color CONFLICT_BITS_COLOR = Color.yellow; + private static final Color BITFIELD_COMPONENT_COLOR = new Color(0xcfcfff); + private static final Color NON_BITFIELD_COMPONENT_COLOR = new Color(0xafafff); + private static final Color INTERIOR_LINE_COLOR = new Color(0xbfbfbf); + + private final Composite composite; + private final boolean bigEndian; + + private int allocationOffset; + private BitFieldAllocation bitFieldAllocation; + + private EditMode editMode = EditMode.NONE; + private int editOrdinal = -1; // FIXME: improve insert use + + BitFieldPlacementComponent(Composite composite) { + this.composite = composite; + bigEndian = composite.getDataOrganization().isBigEndian(); + updatePreferredSize(); + setSize(getPreferredSize()); + setMinimumSize(getPreferredSize()); + ToolTipManager.sharedInstance().registerComponent(this); + } + + private int getPreferredHeight() { + return MY_HEIGHT + SCROLLBAR_THICKNESS; + } + + private int getPreferredWidth() { + if (bitFieldAllocation == null) { + return 10; + } + + int extraLineSpace = BYTE_SEPARATOR_THICKNESS - BIT_SEPARATOR_THICKNESS; + return (bitFieldAllocation.allocationByteSize * BYTE_WIDTH) + BYTE_SEPARATOR_THICKNESS + + extraLineSpace; + } + + public boolean isBigEndian() { + return bigEndian; + } + + public BitFieldAllocation getBitFieldAllocation() { + return bitFieldAllocation; + } + + int getBitOffset(Point point) { + int bitWidthWithLine = BIT_WIDTH + BIT_SEPARATOR_THICKNESS; + int cellIndex = (point.x - BYTE_SEPARATOR_THICKNESS) / bitWidthWithLine; + return (8 * bitFieldAllocation.allocationByteSize) - cellIndex - 1; + } + + private void updatePreferredSize() { + setPreferredSize(new Dimension(getPreferredWidth(), getPreferredHeight())); + revalidate(); + } + + void refresh(int allocationByteSize, int bitSize, int bitOffset) { + bitFieldAllocation = new BitFieldAllocation(allocationByteSize, bitSize, bitOffset); + updatePreferredSize(); + repaint(); + } + + void setAllocationOffset(int allocationOffset) { + this.allocationOffset = allocationOffset; + if (bitFieldAllocation != null) { + bitFieldAllocation.refresh(); + repaint(); + } + } + + int getAllocationOffset() { + return allocationOffset; + } + + void initAdd(int allocationByteSize, int bitSize, int bitOffset) { + editMode = EditMode.ADD; + editOrdinal = -1; + refresh(allocationByteSize, bitSize, bitOffset); + } + + void init(int allocationByteSize, DataTypeComponent editComponent) { + + if (editComponent == null) { + editMode = EditMode.NONE; + editOrdinal = -1; + refresh(allocationByteSize, 0, 0); + return; + } + + // TODO: consider showing a animated hashed-box around original bit boundary + // of the component being modified + + editMode = EditMode.EDIT; + editOrdinal = editComponent.getOrdinal(); + + BitFieldPlacement placement = new BitFieldPlacement(editComponent, allocationByteSize); + bitFieldAllocation = + new BitFieldAllocation(allocationByteSize, placement.rightBit - placement.leftBit + 1, + (8 * allocationByteSize) - placement.rightBit - 1); + updatePreferredSize(); + repaint(); + } + + boolean hasApplyConflict() { + if (composite instanceof Union) { + return false; + } + for (BitAttributes attrs : bitFieldAllocation.bitAttributes) { + if (attrs.hasConflict() && (attrs.isAddBitField() || attrs.isEditField())) { + return true; + } + } + return false; + } + + /** + * @return true if editing or adding a bitfield + */ + boolean isEditing() { + return editMode != EditMode.NONE; + } + + /** + * @return true if adding a bitfield + */ + boolean isAdding() { + return editMode == EditMode.ADD; + } + + void cancelEdit() { + if (editMode != EditMode.NONE) { + editMode = EditMode.NONE; + editOrdinal = -1; + refresh(bitFieldAllocation.allocationByteSize, 0, 0); + } + } + + void componentDeleted(int ordinal) { + if (editMode == EditMode.EDIT) { + if (ordinal == editOrdinal) { + // unexpected removal + editMode = EditMode.ADD; + editOrdinal = -1; + } + else if (ordinal < editOrdinal) { + --editOrdinal; + } + } + bitFieldAllocation.refresh(); + repaint(); + } + + void applyBitField(DataType baseDataType, String fieldName, boolean deleteConflicts, + CompositeChangeListener listener) { + HashSet ordinalDeleteSet = new HashSet<>(); + if (editOrdinal >= 0) { + composite.delete(editOrdinal); + } + if (deleteConflicts) { + for (BitAttributes attrs : bitFieldAllocation.bitAttributes) { + if (attrs.hasConflict() && (attrs.isAddBitField() || attrs.isEditField())) { + // Edit component will always be on top of conflict + ordinalDeleteSet.add(attrs.getConflict().getOrdinal()); + } + } + } + Integer[] ordinalsToDelete = ordinalDeleteSet.toArray(new Integer[ordinalDeleteSet.size()]); + Arrays.sort(ordinalsToDelete); // delete from end first + int ordinal = composite.getNumComponents(); + for (int i = ordinalsToDelete.length - 1; i >= 0; i--) { + ordinal = ordinalsToDelete[i]; + composite.delete(ordinal); + } + + try { + String name = (fieldName != null && fieldName.length() != 0) ? fieldName : null; + DataTypeComponent dtc; + if (composite instanceof Union) { + dtc = composite.insertBitField(ordinal, bitFieldAllocation.allocationByteSize, + bitFieldAllocation.bitOffset, baseDataType, bitFieldAllocation.bitSize, name, + null); + } + else { + Structure struct = (Structure) composite; + dtc = struct.insertBitFieldAt(allocationOffset, + bitFieldAllocation.allocationByteSize, bitFieldAllocation.bitOffset, + baseDataType, bitFieldAllocation.bitSize, name, null); + } + if (listener != null) { + listener.componentChanged(dtc.getOrdinal()); + } + } + catch (ArrayIndexOutOfBoundsException | InvalidDataTypeException e) { + Msg.error(this, "Unexpected bitfield apply error", e); + } + finally { + editMode = EditMode.NONE; + editOrdinal = -1; + bitFieldAllocation.refresh(); + repaint(); + } + } + + BitAttributes getBitAttributes(Point p) { + if (bitFieldAllocation == null) { + return null; + } + for (BitAttributes attrs : bitFieldAllocation.bitAttributes) { + if (attrs.rectangle != null && attrs.rectangle.contains(p)) { + return attrs; + } + } + return null; + } + + @Override + public String getToolTipText(MouseEvent e) { + BitAttributes attrs = getBitAttributes(e.getPoint()); + return attrs != null ? attrs.getTip() : null; + } + + @Override + public void paintComponent(Graphics g) { + + //super.paintComponent(g); + + int height = getHeight(); + int width = getWidth(); + + g.setColor(getBackground()); + g.fillRect(0, 0, width, height); + + if (bitFieldAllocation == null) { + return; + } + + width = getPreferredWidth(); + height = MY_HEIGHT; + + g.setColor(LINE_COLOR); + g.fillRect(0, 0, width, BYTE_SEPARATOR_THICKNESS); // top line + g.fillRect(0, 0, BYTE_SEPARATOR_THICKNESS, height); // left line (full height) + g.fillRect(width - BYTE_SEPARATOR_THICKNESS, 0, BYTE_SEPARATOR_THICKNESS, height); // right line (full height) + int y = CELL_HEIGHT + BYTE_SEPARATOR_THICKNESS; + g.fillRect(0, y, width, BYTE_SEPARATOR_THICKNESS); // next horizontal line + y += CELL_HEIGHT + BYTE_SEPARATOR_THICKNESS; + g.fillRect(0, y, width, BYTE_SEPARATOR_THICKNESS); // bottom line + + paintByteHeader(g, BYTE_SEPARATOR_THICKNESS, allocationOffset); + paintBits((Graphics2D) g, (2 * BYTE_SEPARATOR_THICKNESS) + CELL_HEIGHT); + } + + private void paintByteHeader(Graphics g, int y, int baseOffset) { + int byteSize = bitFieldAllocation.allocationByteSize; + int x = BYTE_SEPARATOR_THICKNESS; + for (int i = 0; i < byteSize; i++) { + // last byte header needs to slightly wider + int w = BYTE_WIDTH; + if (i == (byteSize - 1)) { + w += BYTE_SEPARATOR_THICKNESS - BIT_SEPARATOR_THICKNESS; + } + paintByte(g, x, y, w, i, baseOffset); + x += w; + g.fillRect(x - BYTE_SEPARATOR_THICKNESS, y, BYTE_SEPARATOR_THICKNESS, CELL_HEIGHT); // line after each byte + } + } + + private void paintByte(Graphics g, int x, int y, int width, int byteIndex, int baseOffset) { + + Color curColor = g.getColor(); + Font curFont = g.getFont(); + + int offset = byteIndex; + if (!bigEndian) { + offset = bitFieldAllocation.allocationByteSize - byteIndex - 1; + } + offset += baseOffset; + + g.setColor(BYTE_HEADER_COLOR); + g.fillRect(x, y, width - BYTE_SEPARATOR_THICKNESS, CELL_HEIGHT); // byte fill + + g.setColor(TEXT_COLOR); + Font textFont = getFont().deriveFont(Font.BOLD); + g.setFont(textFont); + + String offsetStr = Integer.toString(offset); + FontMetrics fontMetrics = g.getFontMetrics(); + int textY = y + (CELL_HEIGHT + fontMetrics.getMaxAscent()) / 2; + int textX = x + (width - BYTE_SEPARATOR_THICKNESS - fontMetrics.stringWidth(offsetStr)) / 2; + g.drawString(offsetStr, textX, textY); + + g.setColor(curColor); + g.setFont(curFont); + } + + private void paintBits(Graphics2D g, int y) { + + Color curColor = g.getColor(); + + BitAttributes[] bitAttributes = bitFieldAllocation.bitAttributes; + + int x = BYTE_SEPARATOR_THICKNESS; + + if (bitAttributes[0] != null && bitAttributes[0].leftEndType == EndBitType.TRUNCATED_END) { + // adjust left-most line to reflect truncated component + x -= BIT_SEPARATOR_THICKNESS; // backup to left line location + drawTruncationLine(g, x, y, CELL_HEIGHT); + x += BIT_SEPARATOR_THICKNESS; + } + + BitAttributes prevAttrs = null; + + for (int n = 0; n < bitAttributes.length; n++) { + BitAttributes attrs = bitAttributes[n]; + boolean paintRightLine = n != (bitAttributes.length - 1); + attrs.paint(g, prevAttrs, paintRightLine); + x += attrs.rectangle.width; + prevAttrs = attrs; + } + + if (prevAttrs != null && prevAttrs.rightEndType == EndBitType.TRUNCATED_END) { + x -= BIT_SEPARATOR_THICKNESS; // backup to right line location + drawTruncationLine(g, x, y, CELL_HEIGHT); + } + + g.setColor(curColor); + } + + private static final Stroke DASH = new BasicStroke(1, BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER, 2, new float[] { 3, 3 }, 0); + + private void drawTruncationLine(Graphics2D g, int x, int y, int height) { + + Color c = g.getColor(); + Stroke s = g.getStroke(); + + g.setColor(getBackground()); // draw over black line + g.setStroke(DASH); + g.drawLine(x, y, x, y + height - 1); + + g.setColor(c); + g.setStroke(s); + + } + + private class BitFieldPlacement { + int leftBit; + int rightBit; + boolean truncateLeft; + boolean truncateRight; + boolean zeroBitField; + + BitFieldPlacement(DataTypeComponent component, int allocationByteSize) { + int startOffset = component.getOffset(); + int offsetAdjBytes = startOffset - allocationOffset; + if (!bigEndian) { + offsetAdjBytes = allocationByteSize - offsetAdjBytes - component.getLength(); + } + int leftAdj = 8 * offsetAdjBytes; + if (component.isBitFieldComponent()) { + BitFieldDataType bitfield = (BitFieldDataType) component.getDataType(); + int storageSize = 8 * bitfield.getStorageSize(); + rightBit = leftAdj + storageSize - bitfield.getBitOffset() - 1; + // Use effective bit-size since unaligned uses are only concerned with actual + // bits stored (NOTE: this may cause a transition from declared to effective + // bit-size when editing a bitfield where the these bit-sizes differ). + int bitSize = bitfield.getBitSize(); + if (bitSize == 0) { + zeroBitField = true; + leftBit = rightBit; + } + else { + leftBit = rightBit - bitSize + 1; + } + } + else { + int componentSize = 8 * component.getLength(); + rightBit = leftAdj + componentSize - 1; + leftBit = leftAdj; + } + +// System.out.println(component.toString() + " >>> " + leftBit + " - " + rightBit + +// " oa: " + offsetAdjBytes); + + // clip to allocation region + int allocBitSize = 8 * allocationByteSize; + truncateRight = false; + if (rightBit >= allocBitSize) { + truncateRight = true; + rightBit = allocBitSize - 1; + } + truncateLeft = false; + if (leftBit < 0) { + truncateLeft = true; + leftBit = 0; + } + } + } + + class BitFieldAllocation { + + private final int allocationByteSize; + private int bitSize; + private int bitOffset; + private boolean hasConflict; + + // bit layout normalized to big-endian layout + // left-most allocation msb has array index of 0 + private BitAttributes[] bitAttributes; + + BitFieldAllocation(int allocationByteSize, int bitSize, int bitOffset) { + if (allocationByteSize <= 0 || (bitSize + bitOffset) > (8 * allocationByteSize)) { + throw new IllegalArgumentException("allocation size too small"); + } + this.allocationByteSize = allocationByteSize; + this.bitSize = bitSize; + this.bitOffset = bitOffset; + refresh(); + } + + private void refresh() { + allocateBits(); + layoutBits(); + } + + private void allocateBits() { + bitAttributes = new BitAttributes[8 * allocationByteSize]; + + if (composite instanceof Structure) { + allocateStructureMembers((Structure) composite); + } + + if (editMode != EditMode.NONE) { + int rightMostBit = bitAttributes.length - bitOffset - 1; + if (bitSize == 0) { + allocateZeroBitField(null, rightMostBit); + } + else { + int leftMostBit = rightMostBit - bitSize + 1; + allocateBits(null, leftMostBit, rightMostBit, false, false); + } + } + + // fill-in unallocated bits + for (int i = 0; i < bitAttributes.length; i++) { + if (bitAttributes[i] == null) { + bitAttributes[i] = new BitAttributes(); + } + } + } + + private void layoutBits() { + int x = BYTE_SEPARATOR_THICKNESS; + int y = (2 * BYTE_SEPARATOR_THICKNESS) + CELL_HEIGHT; + int width = BIT_WIDTH + BIT_SEPARATOR_THICKNESS; + for (BitAttributes attrs : bitAttributes) { + attrs.layout(x, y, width, CELL_HEIGHT); + x += width; + } + } + + private void allocateStructureMembers(Structure struct) { + + int allocationEndOffset = allocationOffset + allocationByteSize - 1; + + for (DataTypeComponent component : struct.getDefinedComponents()) { + if (component.getOrdinal() == editOrdinal) { + continue; + } + int startOffset = component.getOffset(); + int endOffset = component.getEndOffset(); + if (endOffset < allocationOffset) { + continue; + } + if (startOffset > allocationEndOffset) { + continue; + } + BitFieldPlacement placement = new BitFieldPlacement(component, allocationByteSize); + if (placement.zeroBitField) { + allocateZeroBitField(component, placement.rightBit); + } + else { + allocateBits(component, placement.leftBit, placement.rightBit, + placement.truncateLeft, placement.truncateRight); + } + } + } + + private void allocateBits(DataTypeComponent dtc, int leftBit, int rightBit, + boolean truncatedLeft, boolean truncatedRight) { + if (truncatedLeft && truncatedRight && leftBit == rightBit) { + throw new AssertException(); + } + int startIndex = Math.max(0, leftBit); + int endIndex = Math.min(bitAttributes.length - 1, rightBit); + for (int i = startIndex; i <= endIndex; i++) { + EndBitType leftEndType = EndBitType.NOT_END; + EndBitType rightEndType = EndBitType.NOT_END; + if (dtc != null) { + if (i == leftBit) { + leftEndType = truncatedLeft ? EndBitType.TRUNCATED_END : EndBitType.END; + } + if (i == rightBit) { + rightEndType = truncatedLeft ? EndBitType.TRUNCATED_END : EndBitType.END; + } + } + bitAttributes[i] = + new BitAttributes(dtc, leftEndType, rightEndType, bitAttributes[i]); + hasConflict |= bitAttributes[i].hasConflict(); + } + } + + private void allocateZeroBitField(DataTypeComponent dtc, int bitIndex) { + bitAttributes[bitIndex] = new BitAttributes(dtc, bitAttributes[bitIndex]); + } + + public int getAllocationByteSize() { + return allocationByteSize; + } + + public int getBitOffset() { + return bitOffset; + } + + public int getBitSize() { + return bitSize; + } + + } + + static enum EditMode { + NONE, ADD, EDIT; + } + + static enum EndBitType { + NOT_END, END, TRUNCATED_END; + } + + class BitAttributes { + + private final DataTypeComponent dtc; + private final EndBitType leftEndType; + private final EndBitType rightEndType; + private final BitAttributes conflict; + + private boolean zeroBitfield; + private boolean unallocated; + + Rectangle rectangle; + + /** + * Unallocated bitfield + * @param dtc + * @param conflict + */ + BitAttributes() { + dtc = null; + leftEndType = EndBitType.NOT_END; + rightEndType = EndBitType.NOT_END; + conflict = null; + unallocated = true; + } + + /** + * Zero-length bitfield + * @param dtc + * @param conflict + */ + BitAttributes(DataTypeComponent dtc, BitAttributes conflict) { + this(dtc, dtc != null ? EndBitType.END : EndBitType.NOT_END, + dtc != null ? EndBitType.END : EndBitType.NOT_END, conflict); + zeroBitfield = true; + } + + BitAttributes(DataTypeComponent dtc, EndBitType leftEndType, EndBitType rightEndType, + BitAttributes conflict) { + this.dtc = dtc; + this.leftEndType = leftEndType; + this.rightEndType = rightEndType; + this.conflict = conflict; + if (conflict != null) { + leftEndType = conflict.leftEndType; + rightEndType = conflict.rightEndType; + } + } + + boolean isAddBitField() { + return !unallocated && dtc == null; + } + + boolean isEditField() { + return dtc != null && dtc.getOrdinal() == editOrdinal; + } + + boolean hasConflict() { + return getConflict() != null; + } + + public DataTypeComponent getConflict() { + BitAttributes c = conflict; + while (c != null && c.dtc.isZeroBitFieldComponent()) { + c = conflict.conflict; + } + return c != null ? c.dtc : null; + } + + void layout(int x, int y, int width, int height) { + rectangle = new Rectangle(x, y, width, height); + } + + void paint(Graphics g, BitAttributes bitAttrsToLeft, boolean paintRightLine) { + // bit box + Color c = getColor(); + g.setColor(c); + g.fillRect(rectangle.x, rectangle.y, BIT_WIDTH, CELL_HEIGHT); + + if (zeroBitfield || + (dtc != null && conflict != null && conflict.dtc.isZeroBitFieldComponent())) { + c = BITFIELD_BITS_COLOR; + Color lineColor = INTERIOR_LINE_COLOR; + if (dtc != null) { + c = BITFIELD_COMPONENT_COLOR; + lineColor = LINE_COLOR; + } + // little-endian: place strip on right-side of bit + // big-endian: place strip on left-side of bit + int xStrip = bigEndian ? rectangle.x : (rectangle.x + BIT_WIDTH - ZERO_BIT_WIDTH); + int xLine = + bigEndian ? (xStrip + ZERO_BIT_WIDTH) : (xStrip - BIT_SEPARATOR_THICKNESS); + g.setColor(c); + g.fillRect(xStrip, rectangle.y, ZERO_BIT_WIDTH, CELL_HEIGHT); + g.setColor(lineColor); + g.fillRect(xLine, rectangle.y, BIT_SEPARATOR_THICKNESS, CELL_HEIGHT); + } + + if (bitAttrsToLeft != null && dtc != null && bitAttrsToLeft.unallocated) { + // draw left bit line if we know better than the undefined to our left + g.setColor(LINE_COLOR); + g.fillRect(rectangle.x - BIT_SEPARATOR_THICKNESS, rectangle.y, + BIT_SEPARATOR_THICKNESS, CELL_HEIGHT); + } + + if (paintRightLine) { + // draw right bit line + Color lineColor = LINE_COLOR; + if (rightEndType == EndBitType.NOT_END) { + lineColor = INTERIOR_LINE_COLOR; + } + g.setColor(lineColor); + g.fillRect(rectangle.x + BIT_WIDTH, rectangle.y, BIT_SEPARATOR_THICKNESS, + CELL_HEIGHT); + } + } + + Color getColor() { + // zero-length stripe will be added later and + // should treated as a conflict + if (unallocated) { + return UNDEFINED_BIT_COLOR; + } + if (conflict != null && !conflict.unallocated) { + if (zeroBitfield) { + return conflict.getColor(); + } + if (!conflict.dtc.isZeroBitFieldComponent()) { + return CONFLICT_BITS_COLOR; + } + } + if (zeroBitfield) { + return UNDEFINED_BIT_COLOR; + } + if (dtc == null) { + return BITFIELD_BITS_COLOR; // edit field + } + return dtc.isBitFieldComponent() ? BITFIELD_COMPONENT_COLOR + : NON_BITFIELD_COMPONENT_COLOR; + } + + String getTip() { + if (dtc == null) { + return null; + } + String name = dtc.getFieldName(); + return dtc.getDataType().getDisplayName() + + (name != null ? (" " + dtc.getFieldName()) : ""); + } + + DataTypeComponent getDataTypeComponent(boolean ignoreEditComponent) { + if (dtc != null && (dtc.getOrdinal() != editOrdinal || !ignoreEditComponent)) { + return dtc; + } + if (conflict != null) { + return conflict.dtc; + } + return null; + } + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java index 19e3fad02f..d4b6738d58 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java @@ -457,8 +457,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { protected abstract DataTypeComponent insert(int rowIndex, DataType dataType, int length, String name, String comment) throws InvalidDataTypeException; - protected abstract void insert(int rowIndex, DataType dataType, int length, String name, - String comment, int numCopies) throws InvalidDataTypeException; + protected abstract void insert(int rowIndex, DataType dataType, int length, int numCopies) throws InvalidDataTypeException; /** * Add a DataType component into to an editable structure @@ -479,7 +478,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { return; } - insert(rowIndex, dataType, dtLen, null, null, multiple); + insert(rowIndex, dataType, dtLen, multiple); } /* (non-Javadoc) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeChangeListener.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeChangeListener.java new file mode 100644 index 0000000000..a4cfd17d01 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeChangeListener.java @@ -0,0 +1,26 @@ +/* ### + * 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.compositeeditor; + +public interface CompositeChangeListener { + + /** + * Indicates the ordinal of the component which has been added, updated or cleared. + * @param ordinal component ordinal + */ + void componentChanged(int ordinal); + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java index 2275f80eb3..c8b8fb9ec9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorAction.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +15,18 @@ */ package ghidra.app.plugin.core.compositeeditor; - public interface EditorAction extends CompositeEditorModelListener { static final String BASIC_ACTION_GROUP = "1_BASIC_EDITOR_ACTION"; static final String FAVORITES_ACTION_GROUP = "2_FAVORITE_DT_EDITOR_ACTION"; static final String CYCLE_ACTION_GROUP = "3_CYCLE_DT_EDITOR_ACTION"; static final String COMPONENT_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION"; - + static final String BITFIELD_ACTION_GROUP = "5_COMPONENT_EDITOR_ACTION"; + /** * Method to set the action's enablement based on the associated editor * model's current state. */ public void adjustEnablement(); - + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java index 1eea9ffea6..4e3682e3fc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java @@ -997,13 +997,14 @@ class StructureEditorModel extends CompEditorModel { } @Override - protected void insert(int rowIndex, DataType dataType, int length, String name, String comment, - int numCopies) throws InvalidDataTypeException { + protected void insert(int rowIndex, DataType dataType, int length, int numCopies) + throws InvalidDataTypeException { checkIsAllowableDataType(dataType, true); int componentOrdinal = convertRowToOrdinal(rowIndex); try { - ((Structure) viewComposite).insert(componentOrdinal, dataType, length, name, comment, - numCopies); + for (int i = 0; i < numCopies; i++) { + viewComposite.insert(componentOrdinal, dataType, length); + } if (rowIndex <= row) { row += numCopies; } @@ -1026,9 +1027,6 @@ class StructureEditorModel extends CompEditorModel { return struct.setFlexibleArrayComponent(dt, dtc.getFieldName(), dtc.getComment()); } - /* (non-Javadoc) - * @see ghidra.app.plugin.datamanager.editor.CompositeEditorModel#replace(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override protected DataTypeComponent replace(int rowIndex, DataType dataType, int length, String name, String comment) throws InvalidDataTypeException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java index 35163715a5..09385c0678 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java @@ -15,11 +15,10 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.framework.plugintool.Plugin; -import ghidra.program.model.data.Structure; - import javax.swing.ImageIcon; +import ghidra.framework.plugintool.Plugin; +import ghidra.program.model.data.Structure; import resources.ResourceManager; /** @@ -45,44 +44,44 @@ public class StructureEditorProvider extends CompositeEditorProvider { editorModel.selectionChanged(); } - /* (non-Javadoc) - * @see ghidra.app.plugin.datamanager.editor.EditorProvider#getName() - */ @Override public String getName() { return "Structure Editor"; } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#createActions() - */ @Override protected CompositeEditorTableAction[] createActions() { + //@formatter:off return new CompositeEditorTableAction[] { new ApplyAction(this), -// new ToggleLockAction(this), - new InsertUndefinedAction(this), new MoveUpAction(this), new MoveDownAction(this), - new ClearAction(this), new DuplicateAction(this), new DuplicateMultipleAction(this), - new DeleteAction(this), new PointerAction(this), new ArrayAction(this), - new ShowComponentPathAction(this), new UnpackageAction(this), - new EditComponentAction(this), new EditFieldAction(this), new HexNumbersAction(this), - new CreateInternalStructureAction(this) }; +// new ToggleLockAction(this), + new InsertUndefinedAction(this), + new MoveUpAction(this), + new MoveDownAction(this), + new ClearAction(this), + new DuplicateAction(this), + new DuplicateMultipleAction(this), + new DeleteAction(this), + new PointerAction(this), + new ArrayAction(this), + new ShowComponentPathAction(this), + new UnpackageAction(this), + new EditComponentAction(this), + new EditFieldAction(this), + new HexNumbersAction(this), + new CreateInternalStructureAction(this), + new AddBitFieldAction(this) + }; + //@formatter:on } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpName() - */ @Override public String getHelpName() { return "Structure_Editor"; } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpTopic() - */ @Override public String getHelpTopic() { return "DataTypeEditors"; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java index f45aa40b47..256087944b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java @@ -410,10 +410,10 @@ class UnionEditorModel extends CompEditorModel { } @Override - public void insert(int rowIndex, DataType dataType, int length, String name, String comment, - int numCopies) throws InvalidDataTypeException { + public void insert(int rowIndex, DataType dataType, int length, int numCopies) + throws InvalidDataTypeException { for (int ii = 0; ii < numCopies; ++ii) { - insert(rowIndex + ii, dataType, length, name, comment); + insert(rowIndex + ii, dataType, length, null, null); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java index b129c645d8..ce42abd343 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java @@ -15,11 +15,10 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.framework.plugintool.Plugin; -import ghidra.program.model.data.Union; - import javax.swing.ImageIcon; +import ghidra.framework.plugintool.Plugin; +import ghidra.program.model.data.Union; import resources.ResourceManager; /** @@ -49,32 +48,34 @@ public class UnionEditorProvider extends CompositeEditorProvider { return "Union Editor"; } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#createActions() - */ @Override protected CompositeEditorTableAction[] createActions() { - return new CompositeEditorTableAction[] { new ApplyAction(this), new MoveUpAction(this), - new MoveDownAction(this), new DuplicateAction(this), new DuplicateMultipleAction(this), - new DeleteAction(this), new PointerAction(this), new ArrayAction(this), - new ShowComponentPathAction(this), new EditComponentAction(this), - new EditFieldAction(this), new HexNumbersAction(this) }; + //@formatter:off + return new CompositeEditorTableAction[] { + new ApplyAction(this), + new MoveUpAction(this), + new MoveDownAction(this), + new DuplicateAction(this), + new DuplicateMultipleAction(this), + new DeleteAction(this), + new PointerAction(this), + new ArrayAction(this), + new ShowComponentPathAction(this), + new EditComponentAction(this), + new EditFieldAction(this), + new HexNumbersAction(this), + new AddBitFieldAction(this) + }; + //@formatter:on } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpName() - */ @Override public String getHelpName() { return "Structure_Editor"; } - /* (non-Javadoc) - * @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpTopic() - */ @Override public String getHelpTopic() { return "DataTypeEditors"; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java index ed5da92228..8b9fc29d34 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java @@ -71,7 +71,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { private GoToService goToService; private DockingAction addAction; private DockingAction deleteAction; - private DataTypeManager dataTypeManager; + private LayeredDataTypeManager dataTypeManager; private Program activeProgram; private SwingUpdateManager updateManager = new SwingUpdateManager(650, () -> updatePreview()); @@ -156,7 +156,33 @@ public class DataTypePreviewPlugin extends ProgramPlugin { updateModel(); } - void updateModel() { + private List getModelDataTypePaths() { + // retain order as they currently exist within model + List list = new ArrayList<>(); + for (Preview preview : model.getModelData()) { + if (preview instanceof DataTypePreview) { + list.add(preview.getDataType().getDataTypePath()); + } + else if (preview instanceof DataTypeComponentPreview) { + DataTypeComponentPreview componentPreview = (DataTypeComponentPreview) preview; + if (componentPreview.getParent() == null) { + list.add(preview.getDataType().getDataTypePath()); + } + } + } + return list; + } + + private void updateModel() { + + // NOTE: data types do not respond to switching the data organization object + // since this is cached internal to the data type at time of construction. + // We must purge old datatypes and have them re-instantiated by the + // datatype manager + List dtPaths = getModelDataTypePaths(); + model.removeAll(); + dataTypeManager.invalidate(); + int transactionId = dataTypeManager.startTransaction("realign"); try { Iterator allComposites = dataTypeManager.getAllComposites(); @@ -164,14 +190,19 @@ public class DataTypePreviewPlugin extends ProgramPlugin { Composite composite = allComposites.next(); if (composite.isInternallyAligned()) { composite.realign(); - model.removeAll(composite); - model.add(composite); } } } finally { dataTypeManager.endTransaction(transactionId, true); } + + for (DataTypePath dtPath : dtPaths) { + DataType dataType = dataTypeManager.getDataType(dtPath); + if (dataType != null) { + model.add(dataType); + } + } } @Override @@ -693,5 +724,9 @@ public class DataTypePreviewPlugin extends ProgramPlugin { return super.getDataOrganization(); } + void invalidate() { + invalidateCache(); + } + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java index 9b18d8c9c3..c4d2ab80cf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java @@ -192,7 +192,7 @@ public abstract class BiDirectionDataType extends StructureDataType protected void shiftOffsets(int startIndex, int endIndex, int deltaOrdinal, int deltaOffset) { for (int i = startIndex; i <= endIndex && i < components.size(); i++) { DataTypeComponentImpl dtc = components.get(i); - shiftOffsets(dtc, deltaOrdinal, deltaOffset); + shiftOffset(dtc, deltaOrdinal, deltaOffset); } } @@ -242,7 +242,7 @@ public abstract class BiDirectionDataType extends StructureDataType } @Override - public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, + public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length, String newName, String comment) { if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) { throw new IllegalArgumentException( @@ -297,11 +297,6 @@ public abstract class BiDirectionDataType extends StructureDataType return dtc; } - @Override - public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length) { - return insertAtOffset(offset, dataType, length, null, null); - } - @Override public DataTypeComponent add(DataType dataType, int length, String newName, String comment) { return addPositive(dataType, length, newName, comment); @@ -374,7 +369,7 @@ public abstract class BiDirectionDataType extends StructureDataType structLength += absAmount; notifySizeChanged(); } - + @Override public DataTypeComponent insert(int index, DataType dataType, int length, String newName, String comment) { @@ -407,12 +402,6 @@ public abstract class BiDirectionDataType extends StructureDataType // return dtc; } - @Override - public void insert(int ordinal, DataType dataType, int length, String name, String comment, - int numCopies) { - throw new AssertException("BiDirectionDataType.insert() not implemented."); - } - protected void insertAtOffset(int offset, int numBytes) { if (offset < splitOffset - negativeLength || offset > splitOffset + positiveLength) { throw new IllegalArgumentException("Offset " + offset + @@ -676,7 +665,7 @@ public abstract class BiDirectionDataType extends StructureDataType // dtMgr.dataTypeChanged(this); // } } - + @Override public DataTypeComponent[] getDefinedComponents() { return components.toArray(new DataTypeComponent[components.size()]); @@ -704,12 +693,6 @@ public abstract class BiDirectionDataType extends StructureDataType return replace(origDtc, dataType, length, newName, comment); } - @Override - public DataTypeComponent replace(int index, DataType dataType, int length) { - validateDataType(dataType); - return replace(index, dataType, length, null, null); - } - @Override public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String newName, String comment) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackPieceDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackPieceDataType.java index a01cdf3daf..7a9475fffc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackPieceDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackPieceDataType.java @@ -24,8 +24,6 @@ import ghidra.program.model.pcode.Varnode; import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; -import java.lang.UnsupportedOperationException; - public class StackPieceDataType extends DataTypeImpl { private final Variable variable; @@ -66,13 +64,13 @@ public class StackPieceDataType extends DataTypeImpl { } @Override - public void setName(String name) throws InvalidNameException, DuplicateNameException { + public void setName(String name) throws InvalidNameException { throw new UnsupportedOperationException(); } @Override - public void setNameAndCategory(CategoryPath path, String name) throws InvalidNameException, - DuplicateNameException { + public void setNameAndCategory(CategoryPath path, String name) + throws InvalidNameException, DuplicateNameException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductBuildNumberDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductBuildNumberDataType.java index f3c00001ac..93440803bd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductBuildNumberDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductBuildNumberDataType.java @@ -22,7 +22,7 @@ import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; class MSRichProductBuildNumberDataType extends DataTypeImpl { - + private final CompId compid; public MSRichProductBuildNumberDataType(CompId compid) { @@ -58,7 +58,7 @@ class MSRichProductBuildNumberDataType extends DataTypeImpl { } @Override - public void setName(String name) throws InvalidNameException, DuplicateNameException { + public void setName(String name) throws InvalidNameException { // ignored } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductIDDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductIDDataType.java index 30578eedb6..e6b9f14c50 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductIDDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/MSRichProductIDDataType.java @@ -58,7 +58,7 @@ class MSRichProductIDDataType extends DataTypeImpl { } @Override - public void setName(String name) throws InvalidNameException, DuplicateNameException { + public void setName(String name) throws InvalidNameException { // ignored } @@ -92,7 +92,7 @@ class MSRichProductIDDataType extends DataTypeImpl { public String getRepresentation(MemBuffer buf, Settings settings, int length) { return compid.getProductDescription(); - + } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/RichObjectCountDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/RichObjectCountDataType.java index 4911745b8d..65b4719ecf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/RichObjectCountDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/rich/RichObjectCountDataType.java @@ -58,7 +58,7 @@ class RichObjectCountDataType extends DataTypeImpl { } @Override - public void setName(String name) throws InvalidNameException, DuplicateNameException { + public void setName(String name) throws InvalidNameException { // ignored } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java index 78c5eedcab..bf9ad61042 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java @@ -27,11 +27,8 @@ import ghidra.program.model.data.*; */ public class CompositeHandler { - private DataType lastBitFieldType = null; // type that has a bitfield - private int bitLength = 0; // how many bits have been used - private int anonCnt = 0; // count of anonymous unions created + private Composite parent; // parent container for bitfields - private Composite bitFieldUnion; // artificial union to contain subfields public CompositeHandler(Composite parent) { super(); @@ -42,7 +39,7 @@ public class CompositeHandler { return parent; } - public void add(Declaration dec) { + public void add(Declaration dec) throws IllegalArgumentException { if (dec == null || dec.getDataType() == null) { return; } @@ -52,7 +49,6 @@ public class CompositeHandler { } // not a bitfield, just add the data type to composite if (!dec.isBitField()) { - initialize(); if (dec.isFlexArray() && parent instanceof Structure) { ((Structure) parent).setFlexibleArrayComponent(dec.getDataType(), dec.getName(), dec.getComment()); @@ -62,72 +58,16 @@ public class CompositeHandler { return; } + // add bit-field component DataType dataType = dec.getDataType(); - int bitSize = dec.getBitFieldSize(); - - // if data type different, start new subfield - handleFullBitfieldUnion(dataType, bitSize); - - // add the bitfield to the continer union - String bitoff = - (bitSize == 1 ? "" + bitLength : bitLength + "-" + (bitLength + bitSize - 1)); - bitFieldUnion.add(dataType, dec.getName(), ": bits " + bitoff); - lastBitFieldType = dataType; - bitLength += bitSize; + try { + parent.addBitField(dataType, dec.getBitFieldSize(), dec.getName(), dec.getComment()); + } + catch (InvalidDataTypeException e) { + // TODO Auto-generated catch block + throw new IllegalArgumentException( + "Invalid bitfield " + dec.getName() + " : " + dec.getBitFieldSize()); + } } - /** - * Creates a new bitfield union container if one not created yet or the current - * is full. - * - * @param dataType - type that is about to be added to container - * @param bitSize - */ - private void handleFullBitfieldUnion(DataType dataType, int bitSize) { - if (!bitfieldFull(dataType, bitSize)) { - return; - } - // create an anonymous union to hold sub bitfields - bitFieldUnion = new UnionDataType(parent.getCategoryPath(), - "anon_" + parent.getName() + "_bitfield_" + ++anonCnt); - bitFieldUnion = (Composite) parent.add(bitFieldUnion).getDataType(); - bitLength = 0; - } - - /** - * Check if a new union needs to be created - * - * @param dataType type that will be added to the union - * @param bitSize size of the bitfield to be added - * - * @return true if a new bitfied union needs to be added - */ - private boolean bitfieldFull(DataType dataType, int bitSize) { - if (parent instanceof Union) { - bitFieldUnion = parent; - return false; - } - - // no union yet - if (bitFieldUnion == null) { - return true; - } - - // datatype has changed - if (!dataType.equals(lastBitFieldType)) { - return true; - } - // union has overflowed - return (bitLength + bitSize) > (dataType.getLength() * 8); - } - - /** - * Clears any residual bitfield info so a new bitfield container will - * be created when necessary. - */ - private void initialize() { - lastBitFieldType = null; - bitLength = 0; - bitFieldUnion = null; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java index d0e3651239..25c9b3e46a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java @@ -26,7 +26,7 @@ public class Declaration { private DataType dt; private String name; private String comment; - private int bitSize = 0; + private int bitSize = -1; private boolean flexArray = false; // true if this is a zero size flex array component public Declaration() { @@ -97,9 +97,6 @@ public class Declaration { if (name == null) { return ""; } - if (isBitField()) { - return name + ":" + bitSize; - } return name; } @@ -133,7 +130,7 @@ public class Declaration { * @return true if a bitfield size has been set */ boolean isBitField() { - return bitSize != 0; + return bitSize >= 0; } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java index 423496ca55..edd7e633cf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java @@ -15,14 +15,6 @@ */ package ghidra.app.util.datatype; -import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog; -import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; -import ghidra.app.services.DataTypeManagerService; -import ghidra.framework.plugintool.ServiceProvider; -import ghidra.program.model.data.*; -import ghidra.util.data.DataTypeParser; - -import java.awt.Component; import java.awt.event.*; import javax.swing.*; @@ -31,6 +23,12 @@ import javax.swing.tree.TreePath; import docking.options.editor.ButtonPanelFactory; import docking.widgets.DropDownSelectionTextField; +import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog; +import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; +import ghidra.app.services.DataTypeManagerService; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.data.*; +import ghidra.util.data.DataTypeParser; /** * An editor that is used to show the {@link DropDownSelectionTextField} for the entering of @@ -58,6 +56,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { private JPanel editorPanel; private DropDownSelectionTextField selectionField; + private JButton browseButton; private DataTypeManagerService dataTypeManagerService; private int maxSize = -1; private DataTypeManager dataTypeManager; @@ -109,9 +108,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { } private void init() { - selectionField = - new DropDownSelectionTextField(new DataTypeDropDownSelectionDataModel( - dataTypeManagerService)); + selectionField = new DropDownSelectionTextField<>( + new DataTypeDropDownSelectionDataModel(dataTypeManagerService)); selectionField.addCellEditorListener(new CellEditorListener() { @Override public void editingCanceled(ChangeEvent e) { @@ -128,15 +126,9 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { selectionField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); - JButton browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE); + browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE); browseButton.setToolTipText("Browse the Data Manager"); - browseButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // show the data type manager - showDataTypeBrowser(); - } - }); + browseButton.addActionListener(e -> showDataTypeBrowser()); editorPanel = new JPanel(); editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS)); @@ -196,7 +188,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { * Returns the component that allows the user to edit. * @return the component that allows the user to edit. */ - public Component getEditorComponent() { + public JComponent getEditorComponent() { return editorPanel; } @@ -204,6 +196,10 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { return selectionField; } + public JButton getBrowseButton() { + return browseButton; + } + /** * Sets the initially selected node in the data type tree that the user can choose to * show. @@ -321,11 +317,11 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { // look for the case where the user made a selection from the matching window, but // then changed the text field text. DataType selectedDataType = selectionField.getSelectedValue(); - if (selectedDataType != null && selectionField.getText().equals(selectedDataType.getName())) { + if (selectedDataType != null && + selectionField.getText().equals(selectedDataType.getName())) { DataTypeParser.ensureIsAllowableType(selectedDataType, allowedDataTypes); return true; } - return false; } @@ -346,6 +342,11 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { // TODO: implement in the future to allow the user to create data types private boolean promptUserToCreateDataType() throws InvalidDataTypeException { + if (selectionField.getText().trim().length() == 0) { + // no need to invoke parser on empty string + return false; + } + // we will create new pointer and array types by default DataType newDataType = null; // try { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MUIResourceDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MUIResourceDataType.java index b6e754e4b4..cd1e9a4b82 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MUIResourceDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MUIResourceDataType.java @@ -81,10 +81,10 @@ public class MUIResourceDataType extends DynamicDataType { if (checkMagic(memBuffer)) { StructureDataType sdt = MUIStructureHeader(); tempOffset = addComp(sdt, sdt.getLength(), "muiResourceHeader", - memBuffer.getAddress().add(tempOffset), comps, tempOffset); + memBuffer.getAddress().add(tempOffset), comps, tempOffset); sdt = MUIStructureData(tempOffset, memBuffer, offsets, sizes); tempOffset = addComp(sdt, sdt.getLength(), "muiResourceData", - memBuffer.getAddress().add(tempOffset), comps, tempOffset); + memBuffer.getAddress().add(tempOffset), comps, tempOffset); } else { Msg.debug(this, "Not an MUI resource data type at " + mbIn.getAddress()); @@ -107,7 +107,7 @@ public class MUIResourceDataType extends DynamicDataType { "0x01 = internal, 0x02 = external"); ArrayDataType adt16 = new ArrayDataType(ByteDataType.dataType, 16, 1); - ArrayDataType adt24 = new ArrayDataType(ByteDataType.dataType, 16, 1); + ArrayDataType adt24 = new ArrayDataType(ByteDataType.dataType, 24, 1); struct.add(adt16, 16, "serviceChecksum", ""); struct.add(adt16, 16, "checksum", ""); @@ -195,9 +195,8 @@ public class MUIResourceDataType extends DynamicDataType { private int addComp(DataType dataType, int length, String fieldName, Address address, List comps, int currentOffset) { if (length > 0) { - ReadOnlyDataTypeComponent readOnlyDataTypeComponent = - new ReadOnlyDataTypeComponent(dataType, this, length, comps.size(), currentOffset, - fieldName, null); + ReadOnlyDataTypeComponent readOnlyDataTypeComponent = new ReadOnlyDataTypeComponent( + dataType, this, length, comps.size(), currentOffset, fieldName, null); comps.add(readOnlyDataTypeComponent); currentOffset += length; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java index 73d30e50c6..cc9ac2685b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java @@ -308,7 +308,7 @@ public class BytesFieldFactory extends FieldFactory { } alignSize = (int) (nextComponent.getMinAddress().subtract(data.getMaxAddress())) - 1; } - if (alignSize == 0) { + if (alignSize <= 0) { return null; } int alignmentOffset = data.getParentOffset() + data.getLength(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java index a58089b092..c354ea264d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java @@ -271,8 +271,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener int offset = (int) address.subtract(parent.getMinAddress()); data = parent.getComponentAt(offset); - // Need to handle filler in aligned structures in a special way. - if (data == null && ((Structure) dt).isInternallyAligned()) { + // Need to handle filler in a special way. + if (data == null) { // So look for next non-filler address. offset++; int length = dt.getLength(); @@ -280,7 +280,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener // If not beyond structure's end, check for non-filler. data = parent.getComponentAt(offset); if (data != null) { // Found non filler address so return it. - return parent.getMinAddress().add(offset); + return data.getMinAddress(); } } } @@ -315,9 +315,12 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener } } else { - // otherwise just return the next dataComponent after this one - if (index < parent.getNumComponents() - 1) { - return parent.getComponent(index + 1).getMinAddress(); + while (index < parent.getNumComponents() - 1) { + index++; + Data component = parent.getComponent(index); + if (address.compareTo(component.getMinAddress()) < 0) { + return component.getAddress(); + } } } return null; @@ -369,7 +372,9 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener } else { int offset = (int) addr.subtract(parent.getMinAddress()); - data = parent.getComponentAt(offset - 1); + List componentsContaining = parent.getComponentsContaining(offset - 1); + data = componentsContaining.isEmpty() ? null + : componentsContaining.get(componentsContaining.size() - 1); } if (data == null) { return addr.previous(); @@ -415,14 +420,18 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener } } } - else { - Data tmpData = data.getComponentAt((int) addr.subtract(dataAddr)); - if (tmpData != null) { - if (tmpData.getMinAddress().equals(addr)) { - list.add(tmpData); - } - if (tmpData.getNumComponents() > 0) { - addOpenData(list, tmpData, addr); + else { // Structure + List dataList = data.getComponentsContaining((int) addr.subtract(dataAddr)); + if (dataList != null) { // nested flex-arrays can cause odd behavior + for (Data subData : dataList) { + // The only case where more than one subData exists is for bit-fields. + // Depending upon the packing, bit-fields at different offsets may overlap + if (subData.getMinAddress().equals(addr)) { + list.add(subData); + } + if (subData.getNumComponents() > 0) { + addOpenData(list, subData, addr); + } } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java b/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java index da54b1c107..976abcdb25 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java @@ -15,7 +15,8 @@ */ package ghidra.util.data; -import java.util.*; +import java.util.ArrayList; +import java.util.List; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.services.DataTypeManagerService; @@ -45,7 +46,12 @@ public class DataTypeParser { /** * Only Fixed-length data types and string data types */ - STRINGS_AND_FIXED_LENGTH + STRINGS_AND_FIXED_LENGTH, + /** + * Only Enums, Integer types and those Typedefs based on them + * for use as a bitfield base datatype + */ + BITFIELD_USE } private DataTypeManager sourceDataTypeManager; // may be null @@ -57,7 +63,7 @@ public class DataTypeParser { * A constructor that does not use the source or destination data type managers. In terms of * the source data type manager, this means that all data type managers will be used when * resolving data types. - * + * * @param dataTypeManagerService * @param allowedTypes */ @@ -73,7 +79,7 @@ public class DataTypeParser { * @param destinationDataTypeManager target data-type manager, or null * @param dataTypeManagerService data-type manager tool service, or null * @param allowedTypes constrains which data-types may be parsed - * + * * @see #DataTypeParser(DataTypeManagerService, AllowedDataTypes) */ public DataTypeParser(DataTypeManager sourceDataTypeManager, @@ -118,7 +124,7 @@ public class DataTypeParser { * Parse a data-type string specification using the specified baseDatatype. * @param suggestedBaseDataType base data-type (may be null), this will be used as the base data-type if * its name matches the base name in the specified dataTypeString. - * @param dataTypeString a base data-type followed by a sequence of zero or more pointer/array decorations to be applied. + * @param dataTypeString a base data-type followed by a sequence of zero or more pointer/array decorations to be applied. * The string may start with the baseDataType's name. * @return parsed data-type or null if not found * @throws InvalidDataTypeException if data-type string is invalid or length exceeds specified maxSize @@ -180,6 +186,12 @@ public class DataTypeParser { throw new InvalidDataTypeException("fixed-length or string data-type required"); } break; + case BITFIELD_USE: + if (!BitFieldDataType.isValidBaseDataType(dt)) { + throw new InvalidDataTypeException( + "enum or integer derived data-type required"); + } + break; case ALL: // do nothing break; @@ -334,8 +346,7 @@ public class DataTypeParser { // see if one of the data types belongs to the program or the built in types, where the // program is more important than the builtin - for (Iterator iter = dtList.iterator(); iter.hasNext();) { - DataType dataType = iter.next(); + for (DataType dataType : dtList) { DataTypeManager manager = dataType.getDataTypeManager(); if (manager instanceof BuiltInDataTypeManager) { programDataType = dataType; @@ -350,8 +361,7 @@ public class DataTypeParser { return null; } - for (Iterator iter = dtList.iterator(); iter.hasNext();) { - DataType dataType = iter.next(); + for (DataType dataType : dtList) { // just one non-matching case means that we can't use the program's data type if (!programDataType.isEquivalent(dataType)) { return null; diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index ef4de5e83d..021a493766 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -1674,17 +1674,19 @@ Composite StructOrUnion() : {Composite comp;} { ( ( DeclSpec() )* { comp = new StructureDataType(ANONYMOUS_STRUCT_PREFIX + cnt++, 0); - try { - // always set the packing, because by default structures should be aligned + + // Always set the packing, because by default structures should be aligned // setting 0 turns off packing, but sets structures to be aligned - // TODO: is this correct, should this be changed and controlled by the - // compiler spec. comp.setPackingValue(this.packSize); - } catch (InvalidInputException e1) { - e1.printStackTrace(); } + } | - ( DeclSpec() )* { comp = new UnionDataType(ANONYMOUS_UNION_PREFIX + cnt++); } + ( DeclSpec() )* { comp = new UnionDataType(ANONYMOUS_UNION_PREFIX + cnt++); + + // Always set the packing, because by default structures should be aligned + // setting 0 turns off packing, but sets structures to be aligned. + comp.setPackingValue(this.packSize); + } ) { try { @@ -1813,7 +1815,14 @@ void StructDeclarator(Declaration dt, Composite comp, CompositeHandler composite } }] | - ":" ConstantExpression() + ":" bitSizeObj = ConstantExpression() { + Integer bitSize = getConstantValue(bitSizeObj,0); + if (bitSize != 0) { + throw new ParseException("invalid bit-field declaration: ':" + bitSize); + } + dec = new Declaration(dt); + dec.setBitFieldSize(0); + } ) { try { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/OutgoingFunctionCallNodeTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/OutgoingFunctionCallNodeTest.java index e92b3a8ac8..de437a8a71 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/OutgoingFunctionCallNodeTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/OutgoingFunctionCallNodeTest.java @@ -51,8 +51,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { super(); } - @Before - public void setUp() throws Exception { + @Before + public void setUp() throws Exception { builder = new ToyProgramBuilder("Call Node Test", true); builder.createMemory(".text", "0x0", 0x3000); @@ -64,8 +64,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { node = new OutgoingFunctionCallNode(program, function, source, true, new AtomicInteger(5)); } -@Test - public void testGenerateChildren_SelfRecursiveCall() throws Exception { + @Test + public void testGenerateChildren_SelfRecursiveCall() throws Exception { builder.createMemoryCallReference(nodeAddress, nodeAddress); @@ -73,8 +73,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_CalledFunctionExists() throws Exception { + @Test + public void testGenerateChildren_CalledFunctionExists() throws Exception { String otherAddress = "0x1000"; builder.createEmptyFunction("Function_2", otherAddress, 10, DataType.DEFAULT); builder.createMemoryCallReference(nodeAddress, otherAddress); @@ -84,8 +84,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertEquals("Function_2", children.get(0).getName()); } -@Test - public void testGenerateChildren_CalledFunctionExists_ExternalCall() throws Exception { + @Test + public void testGenerateChildren_CalledFunctionExists_ExternalCall() throws Exception { String otherAddress = "0x1000"; String externalFunctionName = "External_Function"; @@ -100,8 +100,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertEquals(externalFunctionName, children.get(0).getName()); } -@Test - public void testGenerateChildren_CallReference_ExternalFunction_NoFunctionInMemory() + @Test + public void testGenerateChildren_CallReference_ExternalFunction_NoFunctionInMemory() throws Exception { builder.createMemoryCallReference(nodeAddress, "EXTERNAL:00000001"); @@ -111,8 +111,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertEquals("EXTERNAL:00000001", children.get(0).getName()); } -@Test - public void testGenerateChildren_CallReference_ToPointer_ToExternalFunction() throws Exception { + @Test + public void testGenerateChildren_CallReference_ToPointer_ToExternalFunction() throws Exception { // // Function A @@ -137,8 +137,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertEquals(externalFunctionName, children.get(0).getName()); } -@Test - public void testGenerateChildren_CallReference_ToPointer_ToNonExternalFunction() + @Test + public void testGenerateChildren_CallReference_ToPointer_ToNonExternalFunction() throws Exception { // @@ -162,8 +162,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.get(0) instanceof DeadEndNode); } -@Test - public void testGenerateChildren_CallReference_ToPointer_Offcut() throws Exception { + @Test + public void testGenerateChildren_CallReference_ToPointer_Offcut() throws Exception { // // Bad code case; expected reference to pointer, but no data there @@ -179,21 +179,22 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.get(0) instanceof DeadEndNode); } -@Test - public void testGenerateChildren_WriteReference() throws Exception { + @Test + public void testGenerateChildren_WriteReference() throws Exception { // // Have a reference in the function to a place that is not another function, and the // reference is a write reference. No call node is created. // - builder.createMemoryReference(nodeAddress, "0x1000", RefType.WRITE, SourceType.USER_DEFINED); + builder.createMemoryReference(nodeAddress, "0x1000", RefType.WRITE, + SourceType.USER_DEFINED); List children = node.generateChildren(TaskMonitor.DUMMY); assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_ReadReference_NullInstruction() throws Exception { + @Test + public void testGenerateChildren_ReadReference_NullInstruction() throws Exception { // // Have a reference in the function to a place that is not another function, and the @@ -208,8 +209,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { } -@Test - public void testGenerateChildren_ReadReference_NotCallInstruction() throws Exception { + @Test + public void testGenerateChildren_ReadReference_NotCallInstruction() throws Exception { // // Read reference to an instruction with a flow type that is not a call @@ -223,8 +224,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_ReadReference_CallInstruction_InstructionAtToAddress() + @Test + public void testGenerateChildren_ReadReference_CallInstruction_InstructionAtToAddress() throws Exception { // @@ -243,8 +244,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_ReadReference_CallInstruction_ToData_NoReference() + @Test + public void testGenerateChildren_ReadReference_CallInstruction_ToData_NoReference() throws Exception { // @@ -258,8 +259,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_ReadReference_CallInstruction_ToData_NonExternalReference() + @Test + public void testGenerateChildren_ReadReference_CallInstruction_ToData_NonExternalReference() throws Exception { // @@ -277,8 +278,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_NonFunctionSymbol() + @Test + public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_NonFunctionSymbol() throws Exception { // @@ -296,8 +297,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest { assertTrue(children.isEmpty()); } -@Test - public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_FunctionSymbol() + @Test + public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_FunctionSymbol() throws Exception { // diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java index 74ccaf9b06..006ea5d25e 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java @@ -28,6 +28,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.data.Composite.AlignmentType; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.UsrException; +import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitorAdapter; public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { @@ -118,11 +119,16 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { @Test public void testEditedDtCategoryRemoved() throws Exception { + + DataTypeManager dtm = complexUnion.getDataTypeManager(); + Union refUnion = (Union) dtm.getDataType("/testCat/refUnion"); + assertNotNull(refUnion); + Category tempCat; try { startTransaction("Modify Program"); tempCat = pgmRootCat.createCategory("Temp"); - tempCat.moveDataType(complexUnion, DataTypeConflictHandler.DEFAULT_HANDLER); + tempCat.moveDataType(refUnion, DataTypeConflictHandler.DEFAULT_HANDLER); } finally { endTransaction(true); @@ -142,7 +148,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { }); waitForSwing(); - // refUnion* gets removed since it has only a complexUnion* that was removed. + // refUnion* gets removed assertEquals(num - 1, model.getNumComponents()); assertTrue(dt18.isEquivalent(getDataType(18))); assertTrue(dt20.isEquivalent(getDataType(19))); @@ -409,16 +415,31 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { init(complexUnion, pgmTestCat, false); int num = model.getNumComponents(); + // Clone the data types we want to hold onto for comparison later, since reload can close the viewDTM. DataType dt18 = getDataType(18).clone(programDTM); DataType dt20 = getDataType(20).clone(programDTM); - SwingUtilities.invokeLater(() -> complexUnion.getDataTypeManager().remove(complexUnion, - TaskMonitorAdapter.DUMMY_MONITOR)); + + DataTypeManager dtm = complexUnion.getDataTypeManager(); + Union refUnion = (Union) dtm.getDataType("/testCat/refUnion"); + assertNotNull(refUnion); + + SwingUtilities.invokeLater(() -> dtm.remove(refUnion, TaskMonitor.DUMMY)); // remove refUnion waitForSwing(); - // refUnion* gets removed since it has only a complexUnion* that was removed. - assertEquals(num - 1, model.getNumComponents()); + + // refUnion* gets removed (1 component) + num -= 1; + assertEquals(num, model.getNumComponents()); assertTrue(dt18.isEquivalent(getDataType(18))); assertTrue(dt20.isEquivalent(getDataType(19))); + + SwingUtilities.invokeLater( + () -> simpleUnion.getDataTypeManager().remove(simpleUnion, TaskMonitor.DUMMY)); + waitForSwing(); + + // All components (3 total) which were dependent upon simpleUnion are removed + num -= 3; + assertEquals(num, model.getNumComponents()); } finally { cleanup(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java index cc7694f9a1..2a478cac74 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import org.junit.*; +import ghidra.app.events.ProgramActivatedPluginEvent; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datapreview.DataTypePreviewPlugin.DTPPTableModel; @@ -209,12 +210,19 @@ public class DataTypePreviewPluginTest extends AbstractGhidraHeadedIntegrationTe assertEquals("61004D00200065h", model.getValueAt(4, DTPPTableModel.PREVIEW_COL));// 8-byte long at offset 4 assertEquals("72h", model.getValueAt(5, DTPPTableModel.PREVIEW_COL));// 2-byte short at offset 12 + // deactivate program + plugin.getTool().firePluginEvent(new ProgramActivatedPluginEvent("Test", null)); + waitForPostedSwingRunnables(); + + // NOTE: Altering data organization on-the-fly is not supported dataOrganization.setDefaultAlignment(2); dataOrganization.setShortSize(3); dataOrganization.setIntegerSize(3); dataOrganization.setLongSize(6); - plugin.updateModel(); + // activate program + plugin.getTool().firePluginEvent(new ProgramActivatedPluginEvent("Test", program)); + waitForPostedSwingRunnables(); gotoService.goTo(addr(program, 0x100df26)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java index 72f409165b..3b60e55f1c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java @@ -22,7 +22,6 @@ import org.junit.*; import ghidra.app.cmd.memory.MoveBlockListener; import ghidra.app.cmd.memory.MoveBlockTask; import ghidra.program.database.ProgramBuilder; -import ghidra.program.database.ProgramDB; import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; @@ -81,7 +80,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest model.initialize(block); int transactionID = x8051.startTransaction("Set settings"); - DataTypeManagerDB dtm = ((ProgramDB) x8051).getDataManager(); + DataTypeManagerDB dtm = (DataTypeManagerDB) x8051.getDataTypeManager(); for (int i = 0; i < 10; i++) { Address a = getAddr(x8051, "BITS", i); dtm.setStringSettingsValue(a, "color", "red" + i); @@ -197,8 +196,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest waitForCondition(() -> moveCompleted && x8051.canLock()); // make sure settings on data got moved - DataTypeManagerDB dtm = ((ProgramDB) x8051).getDataManager(); - + DataTypeManagerDB dtm = (DataTypeManagerDB) x8051.getDataTypeManager(); for (int i = 0; i < 10; i++) { Address a = getAddr(x8051, "CODE", 0x2000 + i); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java index e192197dab..ce13faf240 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java @@ -439,7 +439,7 @@ public class FollowFlowProgramBuilder extends ProgramBuilder { startTransaction(); ProgramDB program = getProgram(); Listing listing = program.getListing(); - Structure struct = new StructureDataType(name, thisStructureSize, program.getDataManager()); + Structure struct = new StructureDataType(name, thisStructureSize, program.getDataTypeManager()); struct.replaceAtOffset(0, new FloatDataType(), 4, null, null); struct.replaceAtOffset(pointerOffset, new Pointer32DataType(), 4, null, null); listing.createData(addr(startOfStruct), struct); @@ -473,7 +473,7 @@ public class FollowFlowProgramBuilder extends ProgramBuilder { startTransaction(); ProgramDB program = getProgram(); Listing listing = program.getListing(); - Structure struct = new StructureDataType(name, thisStructureSize, program.getDataManager()); + Structure struct = new StructureDataType(name, thisStructureSize, program.getDataTypeManager()); struct.replaceAtOffset(0, new FloatDataType(), 4, null, null); struct.replaceAtOffset(pointerOffset, new Pointer32DataType(), 4, null, null); struct.replaceAtOffset(pointerOffset + pointerSize, new Pointer32DataType(), 4, null, null); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java index 25875ce4b8..e9e5ae5bca 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java @@ -441,13 +441,8 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration // Check that more than 2 non-equivalent data types trigger the dialog to appear. // Category secondCategory = rootCategory.createCategory("testCategory2"); - dataType = new CustomDataType(secondCategory.getCategoryPath(), crazyName, 2) { - @Override - public DataTypeManager getDataTypeManager() { - return getProgramDataTypeManager(dataTypeManagers); - } - - }; + dataType = new CustomDataType(secondCategory.getCategoryPath(), crazyName, 2, + getProgramDataTypeManager(dataTypeManagers)); addDataType(secondCategory, dataType); showDialogWithoutBlocking(tool, dialog); @@ -1099,6 +1094,10 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration } private class CustomDataType extends StructureDataType { + public CustomDataType(CategoryPath path, String name, int length, DataTypeManager dtm) { + super(path, name, length, dtm); + } + public CustomDataType(CategoryPath path, String name, int length) { super(path, name, length); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java index 5dba108135..d34ca2ef19 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java @@ -55,7 +55,7 @@ public class ArrayTest extends AbstractGhidraHeadedIntegrationTest { public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); listing = program.getListing(); space = program.getAddressFactory().getDefaultAddressSpace(); transactionID = program.startTransaction("Test"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java new file mode 100644 index 0000000000..1886a7178e --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java @@ -0,0 +1,129 @@ +/* ### + * 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.program.database.data; + +import static org.junit.Assert.assertEquals; + +import org.junit.*; + +import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.data.*; +import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.test.TestEnv; +import ghidra.util.task.TaskMonitor; + +public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationTest { + + private ProgramDB program; + private int transactionID; + + private Structure struct; + private AddressSpace space; + private TestEnv env; + private CodeBrowserPlugin plugin; + + @Before + public void setUp() throws Exception { + program = createDefaultProgram("Test", ProgramBuilder._TOY, this); // big-endian + startTransaction(); + + space = program.getAddressFactory().getDefaultAddressSpace(); + + program.getMemory().createInitializedBlock("m", addr(0x1000), 0x100, (byte) 0, + TaskMonitor.DUMMY, false); + + struct = createStructure("Test", 0); + struct.setInternallyAligned(true); + struct.addBitField(IntegerDataType.dataType, 3, "bf1", "Nuts"); + struct.addBitField(IntegerDataType.dataType, 24, "bf2", null); + struct.addBitField(IntegerDataType.dataType, 4, "bf3", null); + struct.addBitField(IntegerDataType.dataType, 12, "bf4", null); + struct.addBitField(IntegerDataType.dataType, 3, "bf4a", null); + struct.addBitField(IntegerDataType.dataType, 3, "bf5", null); + struct.addBitField(IntegerDataType.dataType, 3, "b6", null); + struct.add(new ByteDataType(), "field0", "Comment1"); + struct.add(new WordDataType(), null, "Comment2"); + struct.add(new DWordDataType(), "field3", null); + struct.add(new ByteDataType(), "field4", "Comment4"); + + program.getListing().createData(addr(0x1010), struct); + env = new TestEnv(); + PluginTool tool = env.launchDefaultTool(program); + plugin = getPlugin(tool, CodeBrowserPlugin.class); + } + + private Address addr(long value) { + return space.getAddress(value); + } + + @After + public void tearDown() throws Exception { + endTransaction(); + env.dispose(); + } + + protected Structure createStructure(String name, int length) { + return (Structure) getDataTypeManager().resolve(new StructureDataType(name, length), null); + } + + protected DataTypeManager getDataTypeManager() { + return program.getDataTypeManager(); + } + + private void startTransaction() { + transactionID = program.startTransaction("Test"); + } + + private void endTransaction() { + program.endTransaction(transactionID, true); + } + + @Test + public void testBitField() throws Exception { + openStructure(addr(0x1010)); + assertMnemonic("Test", addr(0x1010), 0); + assertMnemonic("int:3", addr(0x1010), 1); + assertMnemonic("int:24", addr(0x1010), 2); + assertMnemonic("int:4", addr(0x1013), 0); + assertMnemonic("int:12", addr(0x1014), 0); + assertMnemonic("int:3", addr(0x1015), 0); + assertMnemonic("int:3", addr(0x1015), 1); + assertMnemonic("int:3", addr(0x1016), 0); + assertMnemonic("db", addr(0x1017), 0); + assertMnemonic("dw", addr(0x1018), 0); + + System.out.println("wait"); + } + + private void assertMnemonic(String expectedValue, Address addr, int occurrence) { + plugin.goToField(addr, "Mnemonic", occurrence, 0, 0); + assertEquals(expectedValue, plugin.getCurrentFieldText()); + } + + private void openStructure(Address address) { + // open the structure + plugin.goToField(address, "+", 0, 0); + click(plugin, 1); + waitForSwing(); + + } + +} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java index 86f4e811b2..b846c40931 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java @@ -73,7 +73,7 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest { public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); eventRecordingListener = new CategoryTestListener(); dataMgr.addDataTypeManagerListener(eventRecordingListener); root = dataMgr.getRootCategory(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java index a5b95e16e8..4906d6d346 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java @@ -59,7 +59,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { @Before public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); startTransaction(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java index edae0146d6..3291c5865a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/DataManagerTest.java @@ -37,7 +37,7 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest { @Before public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); startTransaction(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/EnumTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/EnumTest.java index 2358abc03a..1d1cba2782 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/EnumTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/EnumTest.java @@ -48,7 +48,7 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest { public void setUp() throws Exception { program = createDefaultProgram("Test", ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); transactionID = program.startTransaction("Test"); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/FunctionDefinitionDBTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/FunctionDefinitionDBTest.java index a568a03035..7315e6d303 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/FunctionDefinitionDBTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/FunctionDefinitionDBTest.java @@ -46,7 +46,7 @@ public class FunctionDefinitionDBTest extends AbstractGhidraHeadedIntegrationTes @Before public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dtm = program.getDataManager(); + dtm = program.getDataTypeManager(); startTransaction(); FunctionDefinitionDataType fdt = new FunctionDefinitionDataType("test"); fdt.setComment("My comments"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java index bc83e19768..077e976455 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java @@ -55,7 +55,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); space = program.getAddressFactory().getDefaultAddressSpace(); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); listing = program.getListing(); transactionID = program.startTransaction("Test"); addBlock(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/StructureTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/StructureTest.java deleted file mode 100644 index c7c01bf57d..0000000000 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/StructureTest.java +++ /dev/null @@ -1,1091 +0,0 @@ -/* ### - * 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.program.database.data; - -import static org.junit.Assert.*; - -import org.junit.*; - -import ghidra.program.database.ProgramBuilder; -import ghidra.program.database.ProgramDB; -import ghidra.program.model.data.*; -import ghidra.test.AbstractGhidraHeadedIntegrationTest; -import ghidra.util.task.TaskMonitorAdapter; - -/** - * - */ -public class StructureTest extends AbstractGhidraHeadedIntegrationTest { - private Structure struct; - private ProgramDB program; - private DataTypeManagerDB dataMgr; - private int transactionID; - - /** - * @param arg0 - */ - public StructureTest() { - super(); - } - - private Structure createStructure(String name, int length) { - return (Structure) dataMgr.resolve(new StructureDataType(name, length), null); - } - - private Union createUnion(String name) { - Union unionDt = new UnionDataType(name); - return (Union) dataMgr.addDataType(unionDt, DataTypeConflictHandler.DEFAULT_HANDLER); - } - - private TypeDef createTypeDef(DataType dataType) { - return (TypeDef) dataMgr.resolve( - new TypedefDataType(dataType.getName() + "TypeDef", dataType), null); - } - - private Array createArray(DataType dataType, int numElements) { - return (Array) dataMgr.resolve( - new ArrayDataType(dataType, numElements, dataType.getLength()), null); - } - - private Pointer createPointer(DataType dataType, int length) { - return (Pointer) dataMgr.resolve(new Pointer32DataType(dataType), null); - } - - @Before - public void setUp() throws Exception { - program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); - startTransaction(); - struct = createStructure("Test", 0); - struct.add(new ByteDataType(), "field0", "Comment1"); - struct.add(new WordDataType(), null, "Comment2"); - struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); - } - - @After - public void tearDown() throws Exception { - endTransaction(); - program.release(this); - } - - @Test - public void testAdd() throws Exception { - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(3, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testAdd2() throws Exception { - struct = createStructure("Test", 10); - - assertEquals(10, struct.getLength()); - assertEquals(10, struct.getNumComponents()); - - struct.add(new ByteDataType(), "field0", "Comment1"); - struct.add(new WordDataType(), "field1", "Comment2"); - struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); - - assertEquals(18, struct.getLength()); - assertEquals(14, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - - dtc = struct.getComponent(10); - assertEquals(10, dtc.getOffset()); - assertEquals(10, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(11); - assertEquals(11, dtc.getOffset()); - assertEquals(11, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(12); - assertEquals(13, dtc.getOffset()); - assertEquals(12, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testInsert_beginning() { - struct.insert(0, new FloatDataType()); - assertEquals(12, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals(null, dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(FloatDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(4, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(5, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(4); - assertEquals(11, dtc.getOffset()); - assertEquals(4, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testInsert_end() { - struct.insert(4, new FloatDataType()); - assertEquals(12, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals(null, dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(3, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(4); - assertEquals(8, dtc.getOffset()); - assertEquals(4, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(FloatDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testInsert_middle() { - struct.insert(2, new FloatDataType()); - assertEquals(12, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(3, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(FloatDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(4); - assertEquals(11, dtc.getOffset()); - assertEquals(4, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testInsertWithEmptySpace() { - struct = createStructure("Test", 100); - struct.insert(40, new ByteDataType()); - struct.insert(20, new WordDataType()); - - struct.insert(10, new FloatDataType()); - - assertEquals(107, struct.getLength()); - assertEquals(103, struct.getNumComponents()); - - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(3, comps.length); - - assertEquals(10, comps[0].getOffset()); - assertEquals(10, comps[0].getOrdinal()); - assertEquals(FloatDataType.class, comps[0].getDataType().getClass()); - - assertEquals(24, comps[1].getOffset()); - assertEquals(21, comps[1].getOrdinal()); - assertEquals(WordDataType.class, comps[1].getDataType().getClass()); - - assertEquals(46, comps[2].getOffset()); - assertEquals(42, comps[2].getOrdinal()); - assertEquals(ByteDataType.class, comps[2].getDataType().getClass()); - } - - // test inserting at offset 0 - @Test - public void testInsertAtOffset() { - struct.insertAtOffset(0, new FloatDataType(), 4); - assertEquals(12, struct.getLength()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - - assertEquals(5, comps.length); - - assertEquals(0, comps[0].getOffset()); - assertEquals(0, comps[0].getOrdinal()); - assertEquals(FloatDataType.class, comps[0].getDataType().getClass()); - - assertEquals(4, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - assertEquals(ByteDataType.class, comps[1].getDataType().getClass()); - - assertEquals(5, comps[2].getOffset()); - assertEquals(2, comps[2].getOrdinal()); - assertEquals(WordDataType.class, comps[2].getDataType().getClass()); - - assertEquals(7, comps[3].getOffset()); - assertEquals(3, comps[3].getOrdinal()); - assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); - - } - - // test inserting at offset 1 - @Test - public void testInsertAtOffset1() { - struct.insertAtOffset(1, new FloatDataType(), 4); - assertEquals(12, struct.getLength()); - - DataTypeComponent[] comps = struct.getDefinedComponents(); - - assertEquals(5, comps.length); - - assertEquals(0, comps[0].getOffset()); - assertEquals(0, comps[0].getOrdinal()); - assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); - - assertEquals(1, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); - - assertEquals(5, comps[2].getOffset()); - assertEquals(2, comps[2].getOrdinal()); - assertEquals(WordDataType.class, comps[2].getDataType().getClass()); - - assertEquals(7, comps[3].getOffset()); - assertEquals(3, comps[3].getOrdinal()); - assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); - - } - - // test inserting at offset 1 - @Test - public void testInsertAtOffset2() { - struct.insertAtOffset(2, new FloatDataType(), 4); - assertEquals(13, struct.getLength()); - - DataTypeComponent[] comps = struct.getDefinedComponents(); - - assertEquals(5, comps.length); - - assertEquals(0, comps[0].getOffset()); - assertEquals(0, comps[0].getOrdinal()); - assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); - - assertEquals(2, comps[1].getOffset()); - assertEquals(2, comps[1].getOrdinal()); - assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); - - assertEquals(6, comps[2].getOffset()); - assertEquals(3, comps[2].getOrdinal()); - assertEquals(WordDataType.class, comps[2].getDataType().getClass()); - - assertEquals(8, comps[3].getOffset()); - assertEquals(4, comps[3].getOrdinal()); - assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); - - } - - @Test - public void testInsertAtOffsetPastEnd() { - struct.insertAtOffset(100, new FloatDataType(), 4); - assertEquals(104, struct.getLength()); - } - - @Test - public void testClearComponent() { - struct.clearComponent(0); - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - dtc = struct.getComponent(1); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testClearComponent1() { - struct.clearComponent(1); - assertEquals(8, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent dtc = struct.getComponent(1); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - dtc = struct.getComponent(2); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - dtc = struct.getComponent(0); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - dtc = struct.getComponent(3); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - assertEquals(3, dtc.getOrdinal()); - assertEquals(3, dtc.getOffset()); - } - - @Test - public void testReplaceFailure() {// bigger, no space below - DataTypeComponent dtc = null; - try { - dtc = struct.replace(0, new QWordDataType(), 8); - } - catch (IllegalArgumentException e) { - // Not enough undefined bytes so should throw this. - } - assertNull(dtc); - } - - @Test - public void testReplace1() {// bigger, space below - struct.insert(1, new QWordDataType()); - struct.clearComponent(1); - assertEquals(16, struct.getLength()); - assertEquals(12, struct.getNumComponents()); - - struct.replace(0, new QWordDataType(), 8); - assertEquals(16, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(9, comps[1].getOffset()); - assertEquals(2, comps[1].getOrdinal()); - } - - @Test - public void testReplace2() {// same size - struct.replace(0, new CharDataType(), 1); - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(CharDataType.class, comps[0].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - } - - @Test - public void testReplace3() {// smaller - struct.replace(1, new CharDataType(), 1); - assertEquals(8, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(CharDataType.class, comps[1].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - assertEquals(3, comps[2].getOffset()); - assertEquals(3, comps[2].getOrdinal()); - assertEquals(DWordDataType.class, comps[2].getDataType().getClass()); - } - - @Test - public void testDataTypeReplaced1() {// bigger, space below - Structure struct2 = createStructure("struct2", 3); - Structure struct2A = createStructure("struct2A", 5); - struct.insert(0, DataType.DEFAULT); - struct.insert(3, struct2); - struct.clearComponent(4); - assertEquals(12, struct.getLength()); - assertEquals(9, struct.getNumComponents()); - - struct.dataTypeReplaced(struct2, struct2A); - assertEquals(12, struct.getLength()); - assertEquals(7, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(4, comps[2].getOffset()); - assertEquals(3, comps[2].getOrdinal()); - assertEquals(5, comps[2].getLength()); - assertEquals(11, comps[3].getOffset()); - assertEquals(6, comps[3].getOrdinal()); - - // check that everything got properly written to the database by - // forcing it to reload - - ((StructureDB) struct).setInvalid(); - comps = struct.getDefinedComponents(); - assertEquals(4, comps[2].getOffset()); - assertEquals(3, comps[2].getOrdinal()); - assertEquals(5, comps[2].getLength()); - assertEquals(11, comps[3].getOffset()); - assertEquals(6, comps[3].getOrdinal()); - - } - - @Test - public void testDataTypeReplaced3() {// bigger, no space at end (structure grows) - Structure struct2 = createStructure("struct2", 3); - Structure struct2A = createStructure("struct2A", 5); - struct.add(struct2); - assertEquals(11, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - struct.dataTypeReplaced(struct2, struct2A); - assertEquals(13, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(8, comps[4].getOffset()); - assertEquals(4, comps[4].getOrdinal()); - assertEquals(5, comps[4].getLength()); - } - - @Test - public void testDataTypeSizeChanged() { - Structure struct2 = createStructure("struct2", 3); - struct.add(struct2); - assertEquals(11, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - struct2.add(new WordDataType()); - assertEquals(13, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(8, comps[4].getOffset()); - assertEquals(4, comps[4].getOrdinal()); - assertEquals(5, comps[4].getLength()); - } - - @Test - public void testDataTypeComponentReplaced() {// bigger, no space at end (structure grows) - Structure struct2 = createStructure("struct2", 3); - Structure struct2A = createStructure("struct2A", 5); - struct.add(struct2); - assertEquals(11, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - struct.replace(4, struct2A, struct2A.getLength()); - assertEquals(13, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(8, comps[4].getOffset()); - assertEquals(4, comps[4].getOrdinal()); - assertEquals(5, comps[4].getLength()); - } - - @Test - public void testDataTypeReplaced2() {// smaller, create undefineds - Structure struct2 = createStructure("struct2", 5); - Structure struct2A = createStructure("struct2A", 3); - struct.insert(0, DataType.DEFAULT); - struct.insert(0, DataType.DEFAULT); - struct.insert(4, struct2); - assertEquals(15, struct.getLength()); - assertEquals(7, struct.getNumComponents()); - - struct.dataTypeReplaced(struct2, struct2A); - - assertEquals(15, struct.getLength()); - assertEquals(9, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(5, comps[2].getOffset()); - assertEquals(4, comps[2].getOrdinal()); - assertEquals(3, comps[2].getLength()); - assertEquals(10, comps[3].getOffset()); - assertEquals(7, comps[3].getOrdinal()); - } - - @Test - public void testDelete() { - struct.delete(1); - assertEquals(6, struct.getLength()); - assertEquals(3, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - } - - @Test - public void testDeleteAtOffset() { - struct.deleteAtOffset(2); - assertEquals(6, struct.getLength()); - assertEquals(3, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - } - - @Test - public void testDeleteComponent() { - Structure s = new StructureDataType("test1", 0); - s.add(new ByteDataType()); - s.add(new FloatDataType()); - - struct.add(s); - - DataType dt = struct.getDataTypeManager().getDataType(struct.getCategoryPath(), "test1"); - assertNotNull(dt); - - DataTypeComponent[] dtc = struct.getComponents(); - assertEquals(5, dtc.length); - - dt.getDataTypeManager().remove(dt, new TaskMonitorAdapter()); - dtc = struct.getComponents(); - assertEquals(9, dtc.length); - - assertEquals(9, struct.getNumComponents()); - } - - @Test - public void testDeleteAll() { - - Structure s = new StructureDataType("test1", 0); - s.add(new ByteDataType()); - s.add(new FloatDataType()); - - struct.add(s); - - DataType dt = struct.getDataTypeManager().getDataType(struct.getCategoryPath(), "test1"); - s = (Structure) dt; - s.deleteAll(); - assertEquals(1, s.getLength()); - assertTrue(s.isNotYetDefined()); - assertEquals(0, s.getNumComponents()); - } - - @Test - public void testGetComponentAt() { - DataTypeComponent dtc = struct.getComponentAt(4); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - assertEquals(2, dtc.getOrdinal()); - assertEquals(3, dtc.getOffset()); - } - - @Test - public void testGetDataTypeAt() { - Structure s1 = createStructure("Test1", 0); - s1.add(new WordDataType()); - s1.add(struct); - s1.add(new ByteDataType()); - - DataTypeComponent dtc = s1.getComponentAt(7); - assertEquals(struct, dtc.getDataType()); - dtc = s1.getDataTypeAt(7); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testAddVarLengthDataTypes() { - Structure s1 = createStructure("Test1", 0); - s1.add(new StringDataType(), 5); - s1.add(new StringDataType(), 10); - s1.add(new StringDataType(), 15); - - DataTypeComponent dtc = s1.getComponentAt(5); - DataType dt = dtc.getDataType(); - assertEquals(-1, dt.getLength()); - assertEquals(10, dtc.getLength()); - assertEquals("string", dt.getDisplayName()); - - } - - @Test - public void testReplaceVarLengthDataTypes() { - Structure s1 = new StructureDataType("Test1", 25); - - s1.replaceAtOffset(0, new StringDataType(), 5, null, null); - s1.replaceAtOffset(5, new StringDataType(), 10, null, null); - s1.replaceAtOffset(15, new StringDataType(), 10, null, null); - - dataMgr.getRootCategory(); - s1 = (Structure) dataMgr.resolve(s1, null); - - DataTypeComponent dtc = s1.getComponentAt(5); - DataType dt = dtc.getDataType(); - assertEquals(-1, dt.getLength()); - assertEquals(10, dtc.getLength()); - assertEquals("string", dt.getDisplayName()); - } - - @Test - public void testReplaceVarLengthDataTypes2() { - - Structure s1 = new StructureDataType("Test1", 0x60); - s1.replaceAtOffset(0, new StringDataType(), 0xd, null, null); - - s1.replaceAtOffset(0xd, new StringDataType(), 0xd, null, null); - s1.replaceAtOffset(0x19, new StringDataType(), 0xc, null, null); - s1.replaceAtOffset(0x24, new StringDataType(), 0xb, null, null); - s1.replaceAtOffset(0x31, new StringDataType(), 0xd, null, null); - s1.replaceAtOffset(0x3e, new StringDataType(), 0xa, null, null); - s1.replaceAtOffset(0x48, new StringDataType(), 0xb, null, null); - s1.replaceAtOffset(0x53, new StringDataType(), 0xd, null, null); - - dataMgr.getRootCategory(); - s1 = (Structure) dataMgr.resolve(s1, null); - - DataTypeComponent dtc = s1.getComponentAt(0); - DataType dt = dtc.getDataType(); - assertEquals("string", dt.getDisplayName()); - assertEquals(0xd, dtc.getLength()); - - dtc = s1.getComponentAt(0x31); - dt = dtc.getDataType(); - assertEquals("string", dt.getDisplayName()); - assertEquals(0xd, dtc.getLength()); - } - - @Test - public void testSetName() throws Exception { - Structure s1 = new StructureDataType("Test1", 0); - s1.add(new StringDataType(), 5); - s1.add(new StringDataType(), 10); - s1.add(new StringDataType(), 15); - s1.add(new ByteDataType()); - - Structure s = (Structure) dataMgr.resolve(s1, null); - s.setName("NewName"); - - assertEquals("NewName", s.getName()); - } - - @Test - public void testTypedefName() throws Exception { - Structure s1 = new StructureDataType("Test1", 0); - s1.add(new StringDataType(), 5); - s1.add(new StringDataType(), 10); - TypedefDataType typdef = new TypedefDataType("TypedefToTest1", s1); - TypeDef td = (TypeDef) dataMgr.resolve(typdef, null); - assertEquals("TypedefToTest1", td.getName()); - - td.setName("NewTypedef"); - assertEquals("NewTypedef", td.getName()); - } - - /** - * Test that a structure can't be added to itself. - */ - @Test - public void testCyclicDependencyProblem1() { - try { - struct.add(struct); - Assert.fail("Shouldn't be able to add a structure to itself."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, struct); - Assert.fail("Shouldn't be able to insert a structure into itself."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, struct, struct.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with the structure itself."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure array can't be added to the same structure. - */ - @Test - public void testCyclicDependencyProblem2() { - Array array = createArray(struct, 3); - try { - struct.add(array); - Assert.fail("Shouldn't be able to add a structure array to the same structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, array); - Assert.fail("Shouldn't be able to insert a structure array into the same structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, array, array.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with an array of the same structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a typedef of a structure can't be added to the structure. - */ - @Test - public void testCyclicDependencyProblem3() { - TypeDef typeDef = createTypeDef(struct); - try { - struct.add(typeDef); - Assert.fail("Shouldn't be able to add a structure typedef to the typedef's structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, typeDef); - Assert.fail( - "Shouldn't be able to insert a structure typedef into the typedef's structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, typeDef, typeDef.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with the structure's typedef."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain another structure that contains it. - */ - @Test - public void testCyclicDependencyProblem4() { - Structure anotherStruct = createStructure("AnotherStruct", 0); - anotherStruct.add(struct); - try { - struct.add(anotherStruct); - Assert.fail( - "Shouldn't be able to add another structure, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, anotherStruct); - Assert.fail( - "Shouldn't be able to insert another structure, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, anotherStruct, anotherStruct.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with another structure which contains this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain another structure that contains a typedef to it. - */ - @Test - public void testCyclicDependencyProblem5() { - Structure anotherStruct = createStructure("AnotherStruct", 0); - TypeDef typeDef = createTypeDef(struct); - anotherStruct.add(typeDef); - try { - struct.add(anotherStruct); - Assert.fail( - "Shouldn't be able to add another structure, which contains a typedef of this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, anotherStruct); - Assert.fail( - "Shouldn't be able to insert another structure, which contains a typedef of this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, anotherStruct, anotherStruct.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with another structure which contains a typedef of this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain a union that contains that structure. - */ - @Test - public void testCyclicDependencyProblem6() { - Union union = createUnion("TestUnion"); - union.add(struct); - try { - struct.add(union); - Assert.fail( - "Shouldn't be able to add a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, union); - Assert.fail( - "Shouldn't be able to insert a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, union, union.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with a union, which contains this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain a typedef of a union that contains that structure. - */ - @Test - public void testCyclicDependencyProblem7() { - Union union = createUnion("TestUnion"); - union.add(struct); - TypeDef typeDef = createTypeDef(union); - try { - struct.add(typeDef); - Assert.fail( - "Shouldn't be able to add a typedef of a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the union typedef to the struct. - } - try { - struct.insert(0, typeDef); - Assert.fail( - "Shouldn't be able to insert a typedef of a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the union typedef to the struct. - } - try { - struct.replace(0, typeDef, typeDef.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with a typedef of a union, which contains this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct component with the union typedef. - } - } - - /** - * Test that a structure can contain a pointer in it to the same structure. - */ - @Test - public void testNoCyclicDependencyProblemForStructurePointer() { - Pointer structurePointer = createPointer(struct, 4); - try { - struct.add(structurePointer); - } - catch (IllegalArgumentException e) { - Assert.fail("Should be able to add a structure pointer to the pointer's structure."); - } - try { - struct.insert(0, structurePointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to insert a structure pointer into the pointer's structure."); - } - try { - struct.replace(0, structurePointer, structurePointer.getLength()); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to replace a structure component with the structure's pointer."); - } - } - - /** - * Test that a structure can contain a pointer in it to a typedef of the same structure. - */ - @Test - public void testNoCyclicDependencyProblemForTypedefPointer() { - TypeDef typeDef = createTypeDef(struct); - Pointer typedefPointer = createPointer(typeDef, 4); - try { - struct.add(typedefPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to add a structure typedef pointer to the pointer's structure."); - } - try { - struct.insert(0, typedefPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to insert a structure typedef pointer into the pointer's structure."); - } - try { - struct.replace(0, typedefPointer, typedefPointer.getLength()); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to replace a structure component with the structure's typedef pointer."); - } - } - - /** - * Test that a structure can contain a pointer in it to a typedef of the same structure. - */ - @Test - public void testNoCyclicDependencyProblemForArrayPointer() { - TypeDef typeDef = createTypeDef(struct); - Array array = createArray(typeDef, 5); - Pointer arrayPointer = createPointer(array, 4); - try { - struct.add(arrayPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to add a structure typedef array pointer to the pointer's structure."); - } - try { - struct.insert(0, arrayPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to insert a structure typedef arrayointer into the pointer's structure."); - } - try { - struct.replace(0, arrayPointer, arrayPointer.getLength()); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to replace a structure component with the structure's typedef array pointer."); - } - } - - private void startTransaction() { - transactionID = program.startTransaction("Test"); - } - - private void endTransaction() { - program.endTransaction(transactionID, true); - } - -} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionDBTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionDBTest.java index 64a9bf7654..0b4d5b9b01 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionDBTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionDBTest.java @@ -629,7 +629,7 @@ public class FunctionDBTest extends AbstractGhidraHeadedIntegrationTest implemen int initialParamCnt = f.getParameterCount(); Structure bar = new StructureDataType("bar", 20); - Pointer barPtr = program.getDataManager().getPointer(bar); + Pointer barPtr = program.getDataTypeManager().getPointer(bar); Parameter returnVar = f.getReturn(); Parameter p1 = f.getParameter(0); @@ -697,7 +697,7 @@ public class FunctionDBTest extends AbstractGhidraHeadedIntegrationTest implemen int initialParamCnt = f.getParameterCount(); Structure bar = new StructureDataType("bar", 20); - Pointer barPtr = program.getDataManager().getPointer(bar); + Pointer barPtr = program.getDataTypeManager().getPointer(bar); Parameter returnVar = f.getReturn(); Parameter p1 = f.getParameter(0); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtilsTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtilsTest.java index 96207b12cf..21324ebbf1 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtilsTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtilsTest.java @@ -114,6 +114,7 @@ public class DataTypeUtilsTest { } private class DataTypeDummy implements DataType { + String wrappedString; UniversalID id; @@ -132,6 +133,11 @@ public class DataTypeUtilsTest { return null; } + @Override + public DataOrganization getDataOrganization() { + throw new UnsupportedOperationException(); + } + @Override public String getDisplayName() { return "This is a wrapper for: " + wrappedString; diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/function/editor/FunctionEditorModelTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/function/editor/FunctionEditorModelTest.java index c0772e9f7f..2f4f23fb10 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/function/editor/FunctionEditorModelTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/function/editor/FunctionEditorModelTest.java @@ -22,6 +22,7 @@ import java.util.List; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; @@ -39,6 +40,7 @@ public class FunctionEditorModelTest extends AbstractGenericTest { private volatile boolean dataChangeCalled; private Structure bigStruct; private ProgramDB program; + private DataTypeManagerService service; private volatile boolean tableRowsChanged; class MyModelChangeListener implements ModelChangeListener { @@ -61,7 +63,6 @@ public class FunctionEditorModelTest extends AbstractGenericTest { program = builder.getProgram(); bigStruct = new StructureDataType("bigStruct", 20); resolveBigStruct(); - model = new FunctionEditorModel(null /* use default parser*/, fun); model.setModelChangeListener(new MyModelChangeListener()); } @@ -73,7 +74,6 @@ public class FunctionEditorModelTest extends AbstractGenericTest { Function fun = builder.createEmptyFunction("bob", "1000", 20, new VoidDataType()); program = builder.getProgram(); resolveBigStruct(); - model = new FunctionEditorModel(null /* use default parser*/, fun); model.setModelChangeListener(new MyModelChangeListener()); } @@ -81,7 +81,7 @@ public class FunctionEditorModelTest extends AbstractGenericTest { private void resolveBigStruct() { int txId = program.startTransaction("Resolve bigStruct"); try { - program.getDataManager().resolve(bigStruct, null); + program.getDataTypeManager().resolve(bigStruct, null); } finally { program.endTransaction(txId, true); @@ -972,9 +972,9 @@ public class FunctionEditorModelTest extends AbstractGenericTest { assertEquals("Stack[0x4]:1", param.getStorage().toString()); DataType struct = new StructureDataType("bigStruct", 100); - DataType structPtr = PointerDataType.getPointer(struct, program.getDataManager()); + DataType structPtr = PointerDataType.getPointer(struct, program.getDataTypeManager()); DataType voidPtr = - PointerDataType.getPointer(VoidDataType.dataType, program.getDataManager()); + PointerDataType.getPointer(VoidDataType.dataType, program.getDataTypeManager()); model.setCallingConventionName(CompilerSpec.CALLING_CONVENTION_thiscall); @@ -1081,9 +1081,10 @@ public class FunctionEditorModelTest extends AbstractGenericTest { assertEquals("CL:1", param.getStorage().toString()); DataType struct = new StructureDataType("bigStruct", 100); - DataType structPtr = PointerDataType.getPointer(struct, program.getDataManager()); + DataType structPtr = PointerDataType.getPointer(struct, program.getDataTypeManager()); + DataType voidPtr = - PointerDataType.getPointer(VoidDataType.dataType, program.getDataManager()); + PointerDataType.getPointer(VoidDataType.dataType, program.getDataTypeManager()); model.setCallingConventionName(CompilerSpec.CALLING_CONVENTION_thiscall); @@ -1722,7 +1723,7 @@ public class FunctionEditorModelTest extends AbstractGenericTest { int txId = program.startTransaction("Add TypeDef jjjjjj"); try { DataType dt = new TypedefDataType("jjjjjj", ByteDataType.dataType); - program.getDataManager().resolve(dt, null); + program.getDataTypeManager().resolve(dt, null); } finally { program.endTransaction(txId, true); @@ -1919,4 +1920,5 @@ public class FunctionEditorModelTest extends AbstractGenericTest { private String getSignatureText() { return model.getFunctionSignatureTextFromModel(); } + } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java index 55e7edcfe9..4e8236cabd 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java @@ -63,7 +63,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { @Before public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); startTransaction(); AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/html/HTMLDataTypeRepresentationTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/html/HTMLDataTypeRepresentationTest.java index 3e9f8afd88..21b15053b3 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/html/HTMLDataTypeRepresentationTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/html/HTMLDataTypeRepresentationTest.java @@ -1407,11 +1407,11 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest { String name = componentAtIndex.getFieldName(); if (optionalName != null) { - destinationComposite.insert(insertIndex, componentCopy, componentCopy.getLength(), + destinationComposite.insert(insertIndex, componentCopy, componentAtIndex.getLength(), optionalName, null); } else { - destinationComposite.insert(insertIndex, componentCopy, componentCopy.getLength(), + destinationComposite.insert(insertIndex, componentCopy, componentAtIndex.getLength(), name + " Copy", null); } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCStructureDBBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCStructureDBBitFieldTest.java new file mode 100644 index 0000000000..fe47ec47c8 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCStructureDBBitFieldTest.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; + +public class MSVCStructureDBBitFieldTest extends MSVCStructureImplBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + getDataTypeManager().startTransaction("Test"); + super.setUp(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureDBBitFieldTest.class) { + if (dataMgr == null) { + dataMgr = new StandAloneDataTypeManager("Test"); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); + } + return dataMgr; + } + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCUnionDBBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCUnionDBBitFieldTest.java new file mode 100644 index 0000000000..9bd6151b54 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCUnionDBBitFieldTest.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; + +public class MSVCUnionDBBitFieldTest extends MSVCUnionImplBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + getDataTypeManager().startTransaction("Test"); + super.setUp(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCUnionDBBitFieldTest.class) { + if (dataMgr == null) { + dataMgr = new StandAloneDataTypeManager("Test"); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); + } + return dataMgr; + } + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBBigEndianBitFieldTest.java new file mode 100644 index 0000000000..1e168359a4 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBBigEndianBitFieldTest.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; + +public class StructureDBBigEndianBitFieldTest extends StructureImplBigEndianBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + getDataTypeManager().startTransaction("Test"); + super.setUp(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureDBBitFieldTest.class) { + if (dataMgr == null) { + dataMgr = new StandAloneDataTypeManager("Test"); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + } + return dataMgr; + } + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBLittleEndianBitFieldTest.java new file mode 100644 index 0000000000..3ad2534133 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBLittleEndianBitFieldTest.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; + +public class StructureDBLittleEndianBitFieldTest extends StructureImplLittleEndianBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + getDataTypeManager().startTransaction("Test"); + super.setUp(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureDBBitFieldTest.class) { + if (dataMgr == null) { + dataMgr = new StandAloneDataTypeManager("Test"); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); + } + return dataMgr; + } + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureImplTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java similarity index 62% rename from Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureImplTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java index 6db2f41cc7..0b1d6bb90b 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureImplTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java @@ -22,21 +22,30 @@ import static org.junit.Assert.*; import org.junit.*; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.program.model.data.*; /** * */ -public class StructureImplTest extends AbstractGenericTest { +public class StructureDataTypeTest extends AbstractGTest { private Structure struct; - /** - * @param arg0 - */ - public StructureImplTest() { - super(); + @Before + public void setUp() throws Exception { + struct = createStructure("TestStruct", 0); + struct.add(new ByteDataType(), "field1", "Comment1"); + struct.add(new WordDataType(), null, "Comment2"); + struct.add(new DWordDataType(), "field3", null); + struct.add(new ByteDataType(), "field4", "Comment4"); + } + + private void transitionToBigEndian() { + + // transition default little-endian structure to big-endian + DataTypeManager beDtm = new MyBigEndianDataTypeManager(); + struct = (Structure) struct.clone(beDtm); } private Structure createStructure(String name, int length) { @@ -56,21 +65,7 @@ public class StructureImplTest extends AbstractGenericTest { } private Pointer createPointer(DataType dataType, int length) { - return new Pointer32DataType(dataType); - } - - @Before - public void setUp() throws Exception { - struct = createStructure("Test", 0); - struct.add(new ByteDataType(), "field0", "Comment1"); - struct.add(new WordDataType(), null, "Comment2"); - struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); - } - - @After - public void tearDown() throws Exception { - + return new PointerDataType(dataType, length); } @Test @@ -81,14 +76,15 @@ public class StructureImplTest extends AbstractGenericTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); + assertEquals("field1", dtc.getFieldName()); assertEquals("Comment1", dtc.getComment()); assertEquals(ByteDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertNull(dtc.getFieldName()); + assertEquals("field_0x1", dtc.getDefaultFieldName()); + assertEquals(null, dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -115,8 +111,8 @@ public class StructureImplTest extends AbstractGenericTest { assertEquals(10, struct.getLength()); assertEquals(10, struct.getNumComponents()); - struct.add(new ByteDataType(), "field0", "Comment1"); - struct.add(new WordDataType(), "field1", "Comment2"); + struct.add(new ByteDataType(), "field1", "Comment1"); + struct.add(new WordDataType(), null, "Comment2"); struct.add(new DWordDataType(), "field3", null); struct.add(new ByteDataType(), "field4", "Comment4"); @@ -126,28 +122,33 @@ public class StructureImplTest extends AbstractGenericTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); + assertEquals("field_0x0", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); + assertNull(dtc.getComment()); assertEquals(DataType.DEFAULT, dtc.getDataType()); dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); + assertEquals("field_0x1", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); + assertEquals(null, dtc.getComment()); assertEquals(DataType.DEFAULT, dtc.getDataType()); dtc = struct.getComponent(10); assertEquals(10, dtc.getOffset()); assertEquals(10, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); + assertEquals("field1", dtc.getFieldName()); assertEquals("Comment1", dtc.getComment()); assertEquals(ByteDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(11); assertEquals(11, dtc.getOffset()); assertEquals(11, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); + assertEquals("field_0xb", dtc.getDefaultFieldName()); + assertNull(dtc.getFieldName()); + assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -168,20 +169,22 @@ public class StructureImplTest extends AbstractGenericTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); - assertEquals(null, dtc.getFieldName()); - assertEquals(null, dtc.getComment()); + assertEquals("field_0x0", dtc.getDefaultFieldName()); + assertNull(dtc.getFieldName()); + assertNull(dtc.getComment()); assertEquals(FloatDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(1); assertEquals(4, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); + assertEquals("field1", dtc.getFieldName()); assertEquals("Comment1", dtc.getComment()); assertEquals(ByteDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(2); assertEquals(5, dtc.getOffset()); assertEquals(2, dtc.getOrdinal()); + assertEquals("field_0x5", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -204,6 +207,7 @@ public class StructureImplTest extends AbstractGenericTest { @Test public void testInsert_end() { + struct.insert(4, new FloatDataType()); assertEquals(12, struct.getLength()); assertEquals(5, struct.getNumComponents()); @@ -211,14 +215,15 @@ public class StructureImplTest extends AbstractGenericTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); + assertEquals("field1", dtc.getFieldName()); assertEquals("Comment1", dtc.getComment()); assertEquals(ByteDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertEquals(null, dtc.getFieldName()); + assertEquals("field_0x1", dtc.getDefaultFieldName()); + assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -239,6 +244,7 @@ public class StructureImplTest extends AbstractGenericTest { dtc = struct.getComponent(4); assertEquals(8, dtc.getOffset()); assertEquals(4, dtc.getOrdinal()); + assertEquals("field_0x8", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals(null, dtc.getComment()); assertEquals(FloatDataType.class, dtc.getDataType().getClass()); @@ -247,6 +253,7 @@ public class StructureImplTest extends AbstractGenericTest { @Test public void testInsert_middle() { + struct.insert(2, new FloatDataType()); assertEquals(12, struct.getLength()); assertEquals(5, struct.getNumComponents()); @@ -254,13 +261,14 @@ public class StructureImplTest extends AbstractGenericTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); - assertEquals("field0", dtc.getFieldName()); + assertEquals("field1", dtc.getFieldName()); assertEquals("Comment1", dtc.getComment()); assertEquals(ByteDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); + assertEquals("field_0x1", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -268,8 +276,9 @@ public class StructureImplTest extends AbstractGenericTest { dtc = struct.getComponent(2); assertEquals(3, dtc.getOffset()); assertEquals(2, dtc.getOrdinal()); + assertEquals("field_0x3", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); + assertNull(dtc.getComment()); assertEquals(FloatDataType.class, dtc.getDataType().getClass()); dtc = struct.getComponent(3); @@ -285,6 +294,7 @@ public class StructureImplTest extends AbstractGenericTest { assertEquals("field4", dtc.getFieldName()); assertEquals("Comment4", dtc.getComment()); assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + } @Test @@ -403,6 +413,304 @@ public class StructureImplTest extends AbstractGenericTest { assertEquals(104, struct.getLength()); } + @Test + public void testInsertBitFieldLittleEndianAppend() throws Exception { + + struct.insertBitField(4, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 undefined 1 null \"\"\n" + + " 11 undefined 1 null \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitField(4, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 8 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 undefined 1 null \"\"\n" + + " 11 undefined 1 null \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldAtLittleEndianAppend() throws Exception { + + struct.insertBitFieldAt(10, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 11 undefined 1 null \"\"\n" + + " 12 undefined 1 null \"\"\n" + + " 13 undefined 1 null \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(10, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 10 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 11 undefined 1 null \"\"\n" + + " 12 undefined 1 null \"\"\n" + + " 13 undefined 1 null \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldAtLittleEndian() throws Exception { + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + try { + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 12, "bf4", "bf4Comment"); + fail( + "expected - IllegalArgumentException: Bitfield does not fit within specified constraints"); + } + catch (IllegalArgumentException e) { + // expected + } + + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldAtBigEndian() throws Exception { + + transitionToBigEndian(); + + try { + struct.insertBitFieldAt(2, 4, 30, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + fail( + "expected - IllegalArgumentException: Bitfield does not fit within specified constraints"); + } + catch (IllegalArgumentException e) { + // expected + } + + struct.insertBitFieldAt(2, 4, 29, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 26, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 11, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(3) 3 bf3 \"bf3Comment\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(3) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(0) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertAtOffsetAfterBeforeBitField() throws Exception { + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + struct.insertAtOffset(2, FloatDataType.dataType, 4); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 float 4 null \"\"\n" + + " 6 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 6 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 6 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 8 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 10 word 2 null \"Comment2\"\n" + + " 12 dword 4 field3 \"\"\n" + + " 16 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 17 Actual Alignment = 1", struct); + //@formatter:on + + } + @Test public void testClearComponent() { struct.clearComponent(0); @@ -444,7 +752,17 @@ public class StructureImplTest extends AbstractGenericTest { } @Test - public void testReplace1() {// bigger, space below + public void testReplace() { // bigger, no space below + try { + struct.replace(0, new QWordDataType(), 8); + Assert.fail(); + } + catch (Exception e) { + } + } + + @Test + public void testReplace1() { // bigger, space below struct.insert(1, new QWordDataType()); struct.clearComponent(1); assertEquals(16, struct.getLength()); @@ -459,7 +777,7 @@ public class StructureImplTest extends AbstractGenericTest { } @Test - public void testReplace2() {// same size + public void testReplace2() { // same size struct.replace(0, new CharDataType(), 1); assertEquals(8, struct.getLength()); assertEquals(4, struct.getNumComponents()); @@ -470,7 +788,7 @@ public class StructureImplTest extends AbstractGenericTest { } @Test - public void testReplace3() {// smaller + public void testReplace3() { // smaller struct.replace(1, new CharDataType(), 1); assertEquals(8, struct.getLength()); assertEquals(5, struct.getNumComponents()); @@ -623,13 +941,66 @@ public class StructureImplTest extends AbstractGenericTest { s.add(new FloatDataType()); struct.add(s); - s.deleteAll(); assertEquals(1, s.getLength()); assertTrue(s.isNotYetDefined()); assertEquals(0, s.getNumComponents()); } + @Test + public void testGetComponents() { + struct = createStructure("Test", 8); + struct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); + struct.insert(5, new WordDataType(), 2, null, "Comment2"); + struct.insert(7, new DWordDataType(), 4, "field8", null); + assertEquals(15, struct.getLength()); + assertEquals(11, struct.getNumComponents()); + DataTypeComponent[] dtcs = struct.getComponents(); + assertEquals(11, dtcs.length); + int offset = 0; + for (int i = 0; i < 11; i++) { + assertEquals(i, dtcs[i].getOrdinal()); + assertEquals(offset, dtcs[i].getOffset()); + offset += dtcs[i].getLength(); + } + assertEquals(DataType.DEFAULT, dtcs[0].getDataType()); + assertEquals(DataType.DEFAULT, dtcs[1].getDataType()); + assertEquals(ByteDataType.class, dtcs[2].getDataType().getClass()); + assertEquals(DataType.DEFAULT, dtcs[3].getDataType()); + assertEquals(DataType.DEFAULT, dtcs[4].getDataType()); + assertEquals(WordDataType.class, dtcs[5].getDataType().getClass()); + assertEquals(DataType.DEFAULT, dtcs[6].getDataType()); + assertEquals(DWordDataType.class, dtcs[7].getDataType().getClass()); + assertEquals(DataType.DEFAULT, dtcs[8].getDataType()); + assertEquals(DataType.DEFAULT, dtcs[9].getDataType()); + assertEquals(DataType.DEFAULT, dtcs[10].getDataType()); + } + + @Test + public void testGetDefinedComponents() { + struct = createStructure("Test", 8); + struct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); + struct.insert(5, new WordDataType(), 2, null, "Comment2"); + struct.insert(7, new DWordDataType(), 4, "field8", null); + assertEquals(15, struct.getLength()); + assertEquals(11, struct.getNumComponents()); + DataTypeComponent[] dtcs = struct.getDefinedComponents(); + assertEquals(3, dtcs.length); + + assertEquals(ByteDataType.class, dtcs[0].getDataType().getClass()); + assertEquals(2, dtcs[0].getOrdinal()); + assertEquals(2, dtcs[0].getOffset()); + + assertEquals(WordDataType.class, dtcs[1].getDataType().getClass()); + assertEquals(5, dtcs[1].getOrdinal()); + assertEquals(5, dtcs[1].getOffset()); + + assertEquals(DWordDataType.class, dtcs[2].getDataType().getClass()); + assertEquals(7, dtcs[2].getOrdinal()); + assertEquals(8, dtcs[2].getOffset()); + + } + @Test public void testGetComponentAt() { DataTypeComponent dtc = struct.getComponentAt(4); @@ -638,19 +1009,6 @@ public class StructureImplTest extends AbstractGenericTest { assertEquals(3, dtc.getOffset()); } - @Test - public void testGetDataTypeAt() { - Structure s1 = createStructure("Test1", 0); - s1.add(new WordDataType()); - s1.add(struct); - s1.add(new ByteDataType()); - - DataTypeComponent dtc = s1.getComponentAt(7); - assertEquals(struct, dtc.getDataType()); - dtc = s1.getDataTypeAt(7); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - } - @Test public void testAddVarLengthDataTypes() { Structure s1 = createStructure("Test1", 0); @@ -732,6 +1090,134 @@ public class StructureImplTest extends AbstractGenericTest { assertEquals("NewTypedef", typdef.getName()); } + @Test + public void testGetDataTypeAt() { + Structure s1 = createStructure("Test1", 0); + s1.add(new WordDataType()); + s1.add(struct); + s1.add(new ByteDataType()); + + DataTypeComponent dtc = s1.getComponentAt(7); + assertEquals(struct, dtc.getDataType()); + dtc = s1.getDataTypeAt(7); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + } + + @Test + public void testReplaceWith() { + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumComponents()); + + Structure newStruct = createStructure("Replaced", 8); + newStruct.setDescription("testReplaceWith()"); + DataTypeComponent dtc0 = newStruct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); + DataTypeComponent dtc1 = newStruct.insert(5, new WordDataType(), 2, null, "Comment2"); + DataTypeComponent dtc2 = newStruct.insert(7, new DWordDataType(), 4, "field8", null); + + struct.replaceWith(newStruct); + assertEquals(15, struct.getLength()); + assertEquals(11, struct.getNumComponents()); + DataTypeComponent[] dtcs = struct.getDefinedComponents(); + assertEquals(3, dtcs.length); + assertEquals(dtc0, dtcs[0]); + assertEquals(dtc1, dtcs[1]); + assertEquals(dtc2, dtcs[2]); + assertEquals("TestStruct", struct.getName()); + assertEquals("", struct.getDescription()); + } + + @Test + public void testReplaceWith2() throws InvalidDataTypeException { + + // NOTE: unaligned bitfields should remain unchanged when + // transitioning endianess even though it makes little sense. + // Unaligned structures are not intended to be portable! + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + + struct.insertBitFieldAt(9, 1, 0, td, 4, "MyBit1", "bitComment1"); + struct.insertBitFieldAt(9, 1, 4, td, 3, "MyBit2", "bitComment2"); + struct.insertBitFieldAt(9, 2, 7, td, 2, "MyBit3", "bitComment3"); + struct.growStructure(1); + + struct.setFlexibleArrayComponent(td, "myFlex", "flexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "Unaligned\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + + " 11 undefined 1 null \"\"\n" + + " Foo[0] 0 myFlex \"flexComment\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + + DataTypeManager beDtm = new MyBigEndianDataTypeManager(); + + Structure newStruct = new StructureDataType("bigStruct", 0, beDtm); + newStruct.replaceWith(struct); + + assertTrue(newStruct.getDataOrganization().isBigEndian()); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/bigStruct\n" + + "Unaligned\n" + + "Structure bigStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + + " 11 undefined 1 null \"\"\n" + + " Foo[0] 0 myFlex \"flexComment\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", newStruct); + //@formatter:on + } + + /** + * Test that a structure can't ... ??? + */ + @Test + public void testCyclingProblem() { + Structure newStruct = createStructure("TestStruct", 80); + newStruct.setDescription("testReplaceWith()"); + newStruct.add(new ByteDataType(), "field0", "Comment1"); + newStruct.add(struct, "field1", null); + newStruct.add(new WordDataType(), null, "Comment2"); + newStruct.add(new DWordDataType(), "field3", null); + + try { + struct.add(newStruct); + Assert.fail(); + } + catch (IllegalArgumentException e) { + } + try { + struct.insert(0, newStruct); + Assert.fail(); + } + catch (IllegalArgumentException e) { + } + try { + struct.replace(0, newStruct, newStruct.getLength()); + Assert.fail(); + } + catch (IllegalArgumentException e) { + } + } + /** * Test that a structure can't be added to itself. */ @@ -1049,4 +1535,13 @@ public class StructureImplTest extends AbstractGenericTest { } } + protected class MyBigEndianDataTypeManager extends StandAloneDataTypeManager { + MyBigEndianDataTypeManager() { + super("BEdtm"); + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + dataOrg.setBigEndian(true); + this.dataOrganization = dataOrg; + } + } + } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBBigEndianBitFieldTest.java new file mode 100644 index 0000000000..935de83bd1 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBBigEndianBitFieldTest.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; + +public class UnionDBBigEndianBitFieldTest extends UnionImplBigEndianBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + getDataTypeManager().startTransaction("Test"); + super.setUp(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureDBBitFieldTest.class) { + if (dataMgr == null) { + dataMgr = new StandAloneDataTypeManager("Test"); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + } + return dataMgr; + } + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBLittleEndianBitFieldTest.java new file mode 100644 index 0000000000..6df4d1a224 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBLittleEndianBitFieldTest.java @@ -0,0 +1,45 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; + +public class UnionDBLittleEndianBitFieldTest extends UnionImplLittleEndianBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + getDataTypeManager().startTransaction("Test"); + super.setUp(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureDBBitFieldTest.class) { + if (dataMgr == null) { + dataMgr = new StandAloneDataTypeManager("Test"); + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); + } + return dataMgr; + } + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java similarity index 67% rename from Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java index 9d22eaf2d1..6f3ab218a8 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java @@ -13,79 +13,59 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* + * + */ package ghidra.program.database.data; import static org.junit.Assert.*; import org.junit.*; -import generic.test.AbstractGenericTest; -import ghidra.program.database.ProgramBuilder; -import ghidra.program.database.ProgramDB; +import generic.test.AbstractGTest; import ghidra.program.model.data.*; -import ghidra.test.AbstractGhidraHeadlessIntegrationTest; /** - * Test the database implementation of Union data type. - * - * + * */ -public class UnionTest extends AbstractGenericTest { +public class UnionDataTypeTest extends AbstractGTest { + private Union union; - private ProgramDB program; - private DataTypeManagerDB dataMgr; - private int transactionID; - - /** - * Constructor for UnionTest. - * @param name - */ - public UnionTest() { - super(); - } - - private Structure createStructure(String name, int length) { - return (Structure) dataMgr.resolve(new StructureDataType(name, length), - DataTypeConflictHandler.DEFAULT_HANDLER); - } - - private Union createUnion(String name) { - return (Union) dataMgr.resolve(new UnionDataType(name), - DataTypeConflictHandler.DEFAULT_HANDLER); - } - - private TypeDef createTypeDef(DataType dataType) { - return (TypeDef) dataMgr.resolve( - new TypedefDataType(dataType.getName() + "TypeDef", dataType), null); - } - - private Array createArray(DataType dataType, int numElements) { - return (Array) dataMgr.resolve( - new ArrayDataType(dataType, numElements, dataType.getLength()), null); - } - - private Pointer createPointer(DataType dataType, int length) { - return (Pointer) dataMgr.resolve(new Pointer32DataType(dataType), null); - } @Before public void setUp() throws Exception { - program = - AbstractGhidraHeadlessIntegrationTest.createDefaultProgram("Test", ProgramBuilder._TOY, this); - dataMgr = program.getDataManager(); - transactionID = program.startTransaction("Test"); - union = createUnion("Test"); + union = createUnion("TestUnion"); union.add(new ByteDataType(), "field1", "Comment1"); union.add(new WordDataType(), null, "Comment2"); union.add(new DWordDataType(), "field3", null); union.add(new ByteDataType(), "field4", "Comment4"); } - @After - public void tearDown() throws Exception { - program.endTransaction(transactionID, true); - program.release(this); + private void transitionToBigEndian() { + // transition default little-endian structure to big-endian + DataTypeManager beDtm = new MyBigEndianDataTypeManager(); + union = (Union) union.clone(beDtm); + } + + private Union createUnion(String name) { + return new UnionDataType(name); + } + + private Structure createStructure(String name, int size) { + return new StructureDataType(name, size); + } + + private TypeDef createTypeDef(DataType dataType) { + return new TypedefDataType(dataType.getName() + "TypeDef", dataType); + } + + private Array createArray(DataType dataType, int numElements) { + return new ArrayDataType(dataType, numElements, dataType.getLength()); + } + + private Pointer createPointer(DataType dataType, int length) { + return new PointerDataType(dataType, length); } @Test @@ -109,12 +89,11 @@ public class UnionTest extends AbstractGenericTest { @Test public void testAdd2() { - Structure struct = new StructureDataType("struct_1", 0); + Structure struct = createStructure("struct_1", 0); struct.add(new ByteDataType()); struct.add(new StringDataType(), 10); union.add(struct); - union.delete(4); assertEquals(4, union.getNumComponents()); assertEquals(4, union.getLength()); @@ -122,7 +101,7 @@ public class UnionTest extends AbstractGenericTest { @Test public void testGetComponent() { - Structure struct = new StructureDataType("struct_1", 0); + Structure struct = createStructure("struct_1", 0); struct.add(new ByteDataType()); struct.add(new StringDataType(), 10); DataTypeComponent newdtc = union.add(struct, "field5", "comments"); @@ -135,7 +114,7 @@ public class UnionTest extends AbstractGenericTest { @Test public void testGetComponents() { - Structure struct = new StructureDataType("struct_1", 0); + Structure struct = createStructure("struct_1", 0); struct.add(new ByteDataType()); struct.add(new StringDataType(), 10); union.add(struct, "field5", "comments"); @@ -147,7 +126,7 @@ public class UnionTest extends AbstractGenericTest { @Test public void testInsert() { - Structure struct = new StructureDataType("struct_1", 0); + Structure struct = createStructure("struct_1", 0); struct.add(new ByteDataType()); struct.add(new StringDataType(), 10); @@ -161,20 +140,92 @@ public class UnionTest extends AbstractGenericTest { } @Test - public void testGetName() { - assertEquals("Test", union.getName()); + public void testBitFieldUnionLength() throws Exception { + + int cnt = union.getNumComponents(); + for (int i = 0; i < cnt; i++) { + union.delete(0); + } + union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment"); + union.insert(0, ShortDataType.dataType); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 short 2 null \"\"\n" + + " 1 byte:2(4) 1 bf1 \"bf1Comment\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on } @Test - public void testClone() throws Exception { + public void testInsertBitFieldLittleEndian() throws Exception { + + union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); + union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 int:4(0) 1 bf1 \"bf1Comment\"\n" + + " 0 byte:4(0) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testInsertBitFieldBigEndian() throws Exception { + + transitionToBigEndian(); + + union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); + union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 int:4(4) 1 bf1 \"bf1Comment\"\n" + + " 0 byte:4(4) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testGetName() { + assertEquals("TestUnion", union.getName()); + } + + @Test + public void testCloneRetainIdentity() throws Exception { Union unionCopy = (Union) union.clone(null); assertNull(unionCopy.getDataTypeManager()); assertEquals(4, union.getLength()); } + @Test + public void testCopyDontRetain() throws Exception { + Union unionCopy = (Union) union.copy(null); + assertNull(unionCopy.getDataTypeManager()); + assertEquals(4, union.getLength()); + } + @Test public void testDelete() throws Exception { - Structure struct = new StructureDataType("struct_1", 0); + Structure struct = createStructure("struct_1", 0); struct.add(new ByteDataType()); struct.add(new StringDataType(), 10); union.add(struct); @@ -189,16 +240,15 @@ public class UnionTest extends AbstractGenericTest { @Test public void testIsPartOf() { - Structure struct = new StructureDataType("struct_1", 0); + Structure struct = createStructure("struct_1", 0); struct.add(new ByteDataType()); - DataTypeComponent dtc = struct.add(new StringDataType(), 10); - DataTypeComponent newdtc = union.add(struct); - dtc = union.getComponent(4); + DataTypeComponent dtc = struct.add(createStructure("mystring", 10)); DataType dt = dtc.getDataType(); + DataTypeComponent newdtc = union.add(struct); assertTrue(union.isPartOf(dt)); Structure newstruct = (Structure) newdtc.getDataType(); - Structure s1 = (Structure) newstruct.add(new StructureDataType("s1", 1)).getDataType(); + Structure s1 = (Structure) newstruct.add(createStructure("s1", 1)).getDataType(); dt = s1.add(new QWordDataType()).getDataType(); assertTrue(union.isPartOf(dt)); @@ -206,18 +256,48 @@ public class UnionTest extends AbstractGenericTest { @Test public void testReplaceWith() { - Structure struct = new StructureDataType("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - Union newunion = new UnionDataType("newunion"); - newunion.add(struct); + assertEquals(4, union.getLength()); + assertEquals(4, union.getNumComponents()); - union.replaceWith(newunion); - assertEquals(1, newunion.getNumComponents()); - DataType dt = dataMgr.getDataType("/struct_1"); - assertNotNull(dt); + Union newUnion = createUnion("Replaced"); + newUnion.setDescription("testReplaceWith()"); + DataTypeComponent dtc2 = newUnion.insert(0, new DWordDataType(), 4, "field2", null); + DataTypeComponent dtc1 = newUnion.insert(0, new WordDataType(), 2, null, "Comment2"); + DataTypeComponent dtc0 = newUnion.insert(0, new ByteDataType(), 1, "field0", "Comment1"); - assertEquals(dt, union.getComponent(0).getDataType()); + union.replaceWith(newUnion); + assertEquals(4, union.getLength()); + assertEquals(3, union.getNumComponents()); + DataTypeComponent[] dtcs = union.getComponents(); + assertEquals(3, dtcs.length); + assertEquals(dtc0, dtcs[0]); + assertEquals(dtc1, dtcs[1]); + assertEquals(dtc2, dtcs[2]); + assertEquals("TestUnion", union.getName()); + assertEquals("", union.getDescription()); // unchanged + } + + @Test + public void testCyclingProblem() { + Union newUnion = createUnion("Test"); + newUnion.setDescription("testReplaceWith()"); + newUnion.add(new ByteDataType(), "field0", "Comment1"); + newUnion.add(union, "field1", null); + newUnion.add(new WordDataType(), null, "Comment2"); + newUnion.add(new DWordDataType(), "field3", null); + + try { + union.add(newUnion); + Assert.fail(); + } + catch (IllegalArgumentException e) { + } + try { + union.insert(0, newUnion); + Assert.fail(); + } + catch (IllegalArgumentException e) { + } } /** @@ -453,4 +533,12 @@ public class UnionTest extends AbstractGenericTest { } } + protected class MyBigEndianDataTypeManager extends StandAloneDataTypeManager { + MyBigEndianDataTypeManager() { + super("BEdtm"); + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + dataOrg.setBigEndian(true); + this.dataOrganization = dataOrg; + } + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java new file mode 100644 index 0000000000..136221cc0a --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java @@ -0,0 +1,77 @@ +/* ### + * 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.program.model.data; + +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.io.InputStream; + +import org.junit.Before; + +import generic.test.AbstractGTest; +import ghidra.app.util.cparser.C.CParser; +import ghidra.util.Msg; +import resources.ResourceManager; + +public abstract class AbstractCompositeImplBitFieldTest extends AbstractGTest { + + protected static final String C_SOURCE_FILE = "ghidra/app/util/cparser/bitfields.h"; + + @Before + public void setUp() throws Exception { + + DataTypeManager dataMgr = getDataTypeManager(); + if (dataMgr.getDataTypeCount(false) != 0) { + Msg.info(this, "Using previously parsed data types"); + return; // already have types + } + + Msg.info(this, "Parsing data types from " + C_SOURCE_FILE); + + CParser parser = new CParser(dataMgr, true, null); + + try (InputStream is = ResourceManager.getResourceAsStream(C_SOURCE_FILE)) { + if (is == null) { + throw new FileNotFoundException("Resource not found: " + C_SOURCE_FILE); + } + Msg.debug(this, "Parsing C headers from " + C_SOURCE_FILE); + parser.parse(is); + } + } + + protected class MyDataTypeManager extends StandAloneDataTypeManager { + MyDataTypeManager(String name, DataOrganization dataOrg) { + super(name); + this.dataOrganization = dataOrg; + } + } + + abstract DataTypeManager getDataTypeManager(); + + Structure getStructure(String name) { + DataType dataType = getDataTypeManager().getDataType("/" + name); + assertTrue("Data type not found: " + name, dataType instanceof Structure); + return (Structure) dataType; + } + + Union getUnion(String name) { + DataType dataType = getDataTypeManager().getDataType("/" + name); + assertTrue("Data type not found: " + name, dataType instanceof Union); + return (Union) dataType; + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java new file mode 100644 index 0000000000..40e5ea56e1 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java @@ -0,0 +1,753 @@ +/* ### + * 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.program.model.data; + +import org.junit.Test; + +public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureImplBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); + dataMgr = new MyDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureBitFieldsB1() { + Structure struct = getStructure("B1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + + "Aligned\n" + + "Structure B1 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2() { + Structure struct = getStructure("B2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + + "Aligned\n" + + "Structure B2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3() { + Structure struct = getStructure("B3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + + "Aligned\n" + + "Structure B3 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1() { + Structure struct = getStructure("Z1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + + "Aligned\n" + + "Structure Z1 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:0(0) 1 \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2() { + Structure struct = getStructure("Z2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + + "Aligned\n" + + "Structure Z2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 int:0(0) 1 \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3() { + Structure struct = getStructure("Z3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + + "Aligned\n" + + "Structure Z3 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + " 7 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4() { + Structure struct = getStructure("Z4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + + "Aligned\n" + + "Structure Z4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5() { + Structure struct = getStructure("Z5"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + + "Aligned\n" + + "Structure Z5 {\n" + + " 0 char 1 a \"\"\n" + + " 8 int:0(0) 1 \"\"\n" + + " 8 longlong:6(0) 1 b \"\"\n" + + " 16 int:8(0) 1 c \"\"\n" + + " 20 char 1 d \"\"\n" + + "}\n" + + "Size = 24 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ6() { + Structure struct = getStructure("Z6"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + + "Aligned\n" + + "Structure Z6 {\n" + + " 0 char 1 a \"\"\n" + + " 8 int:0(0) 1 \"\"\n" + + " 8 longlong:6(0) 1 b \"\"\n" + + " 16 int:8(0) 1 c \"\"\n" + + " 20 char 1 d \"\"\n" + + " 24 longlong:6(0) 1 e \"\"\n" + + " 32 int:8(0) 1 f \"\"\n" + + " 36 char 1 g \"\"\n" + + "}\n" + + "Size = 40 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p1() { + Structure struct = getStructure("B1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + + "Aligned pack(1)\n" + + "Structure B1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p1() { + Structure struct = getStructure("B2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + + "Aligned pack(1)\n" + + "Structure B2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 4 int:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p1() { + Structure struct = getStructure("B3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + + "Aligned pack(1)\n" + + "Structure B3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p1() { + Structure struct = getStructure("Z1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + + "Aligned pack(1)\n" + + "Structure Z1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:0(0) 1 \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p1() { + Structure struct = getStructure("Z2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + + "Aligned pack(1)\n" + + "Structure Z2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 int:0(0) 1 \"\"\n" + + " 7 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1() { + Structure struct = getStructure("Z3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + + "Aligned pack(1)\n" + + "Structure Z3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 4 int:4(0) 1 d \"\"\n" + + " 6 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1T() { + Structure struct = getStructure("Z3p1T"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + + "Aligned\n" + + "Structure Z3p1T {\n" + + " 0 char 1 a \"\"\n" + + " 1 Z3p1 7 z3p1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p1() { + Structure struct = getStructure("Z4p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + + "Aligned pack(1)\n" + + "Structure Z4p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 longlong:0(0) 1 \"\"\n" + + " 7 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p2() { + Structure struct = getStructure("B1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + + "Aligned pack(2)\n" + + "Structure B1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p2() { + Structure struct = getStructure("B2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + + "Aligned pack(2)\n" + + "Structure B2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p2() { + Structure struct = getStructure("B3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + + "Aligned pack(2)\n" + + "Structure B3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB4p2() { + Structure struct = getStructure("B4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + + "Aligned pack(2)\n" + + "Structure B4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong 8 d \"\"\n" + + " 16 int:4(0) 1 e \"\"\n" + + "}\n" + + "Size = 20 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2() { + Structure struct = getStructure("Z1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + + "Aligned pack(2)\n" + + "Structure Z1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:0(0) 1 \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2x() { + Structure struct = getStructure("Z1p2x"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + + "Aligned pack(2)\n" + + "Structure Z1p2x {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:0(0) 1 \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + " 8 short:4(4) 1 d1 \"\"\n" + + " 9 short:4(0) 1 d2 \"\"\n" + + " 9 short:4(4) 1 d3 \"\"\n" + + " 10 short:4(0) 1 d4 \"\"\n" + + " 10 short:4(4) 1 d5 \"\"\n" + + " 11 short:4(0) 1 d6 \"\"\n" + + " 11 short:4(4) 1 d7 \"\"\n" + + " 12 short:0(0) 1 \"\"\n" + + " 12 ushort:6(0) 1 _b \"\"\n" + + " 14 int:8(0) 1 _c \"\"\n" + + " 18 short:4(0) 1 _d \"\"\n" + + " 18 short:4(4) 1 _d1 \"\"\n" + + " 19 short:4(0) 1 _d2 \"\"\n" + + " 19 short:4(4) 1 _d3 \"\"\n" + + " 20 short:4(0) 1 _d4 \"\"\n" + + " 20 short:4(4) 1 _d5 \"\"\n" + + " 21 short:4(0) 1 _d6 \"\"\n" + + " 21 short:4(4) 1 _d7 \"\"\n" + + "}\n" + + "Size = 22 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p2() { + Structure struct = getStructure("Z2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + + "Aligned pack(2)\n" + + "Structure Z2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 int:0(0) 1 \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p2() { + Structure struct = getStructure("Z3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + + "Aligned pack(2)\n" + + "Structure Z3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + " 7 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p2() { + Structure struct = getStructure("Z4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + + "Aligned pack(2)\n" + + "Structure Z4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p2() { + Structure struct = getStructure("Z5p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + + "Aligned pack(2)\n" + + "Structure Z5p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:12(0) 2 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p2() { + Structure struct = getStructure("x1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + + "Aligned pack(2)\n" + + "Structure x1p2 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p2() { + Structure struct = getStructure("x2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + + "Aligned pack(2)\n" + + "Structure x2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p2() { + Structure struct = getStructure("x3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + + "Aligned pack(2)\n" + + "Structure x3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(0) 1 \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p2() { + Structure struct = getStructure("x4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + + "Aligned pack(2)\n" + + "Structure x4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + " 5 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p4() { + Structure struct = getStructure("Z5p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + + "Aligned pack(4)\n" + + "Structure Z5p4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:12(0) 2 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p4() { + Structure struct = getStructure("x1p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + + "Aligned pack(4)\n" + + "Structure x1p4 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p4() { + Structure struct = getStructure("x2p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + + "Aligned pack(4)\n" + + "Structure x2p4 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p4() { + Structure struct = getStructure("x3p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + + "Aligned pack(4)\n" + + "Structure x3p4 {\n" + + " 0 char 1 a \"\"\n" + + " 4 short:0(0) 1 \"\"\n" + + " 4 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p4() { + Structure struct = getStructure("x4p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + + "Aligned pack(4)\n" + + "Structure x4p4 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:27(0) 4 b \"\"\n" + + " 7 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT1() { + Structure struct = getStructure("T1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + + "Aligned\n" + + "Structure T1 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 4 myEnum:3(0) 1 b \"\"\n" + + " 4 enumTypedef:3(3) 1 c \"\"\n" + + " 8 charTypedef:7(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT2() { + Structure struct = getStructure("T2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + + "Aligned\n" + + "Structure T2 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 4 intTypedef:17(0) 3 b \"\"\n" + + " 6 enumTypedef:3(1) 1 c \"\"\n" + + " 8 charTypedef:3(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1() { + Structure struct = getStructure("S1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + + "Aligned\n" + + "Structure S1 {\n" + + " 0 B1 12 b1 \"\"\n" + + " 12 B2 8 b2 \"\"\n" + + " 20 Z1 12 z1 \"\"\n" + + " 32 Z2 12 z2 \"\"\n" + + " 48 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 56 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p1() { + Structure struct = getStructure("S1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + + "Aligned pack(1)\n" + + "Structure S1p1 {\n" + + " 0 B1 12 b1 \"\"\n" + + " 12 B2 8 b2 \"\"\n" + + " 20 Z1 12 z1 \"\"\n" + + " 32 Z2 12 z2 \"\"\n" + + " 44 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 52 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p1() { + Structure struct = getStructure("S2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + + "Aligned pack(1)\n" + + "Structure S2p1 {\n" + + " 0 B1p1 9 b1p1 \"\"\n" + + " 9 B2p1 7 b2p1 \"\"\n" + + " 16 Z1p1 9 z1p1 \"\"\n" + + " 25 Z2p1 9 z2p1 \"\"\n" + + " 34 Z3p1 7 z3p1 \"\"\n" + + "}\n" + + "Size = 41 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p2() { + Structure struct = getStructure("S1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + + "Aligned pack(2)\n" + + "Structure S1p2 {\n" + + " 0 B1 12 b1 \"\"\n" + + " 12 B2 8 b2 \"\"\n" + + " 20 Z1 12 z1 \"\"\n" + + " 32 Z2 12 z2 \"\"\n" + + " 44 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 52 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p2() { + Structure struct = getStructure("S2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + + "Aligned pack(2)\n" + + "Structure S2p2 {\n" + + " 0 B1p2 10 b1p2 \"\"\n" + + " 10 B2p2 8 b2p2 \"\"\n" + + " 18 Z1p2 10 z1p2 \"\"\n" + + " 28 Z2p2 10 z2p2 \"\"\n" + + " 38 Z3p2 8 z3p2 \"\"\n" + + "}\n" + + "Size = 46 Actual Alignment = 2", struct); + //@formatter:on + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java new file mode 100644 index 0000000000..951d422b0f --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java @@ -0,0 +1,108 @@ +/* ### + * 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.program.model.data; + +import org.junit.Test; + +public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCUnionImplBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); + dataMgr = new MyDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testUnionBitFieldsU1() { + Union struct = getUnion("U1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + + "Aligned\n" + + "Union U1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1z() { + Union struct = getUnion("U1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + + "Aligned\n" + + "Union U1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 1 \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1() { + Union struct = getUnion("U1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + + "Aligned pack(1)\n" + + "Union U1p1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1z() { + Union struct = getUnion("U1p1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + + "Aligned pack(1)\n" + + "Union U1p1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 1 \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p2() { + Union struct = getUnion("U1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + + "Aligned pack(2)\n" + + "Union U1p2 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java new file mode 100644 index 0000000000..ab495b8fc6 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java @@ -0,0 +1,755 @@ +/* ### + * 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.program.model.data; + +import org.junit.Test; + +public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { + + // NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2 + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureImplBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + dataMgr = new MyDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureBitFieldsB1() { + Structure struct = getStructure("B1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + + "Aligned\n" + + "Structure B1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2() { + Structure struct = getStructure("B2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + + "Aligned\n" + + "Structure B2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3() { + Structure struct = getStructure("B3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + + "Aligned\n" + + "Structure B3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1() { + Structure struct = getStructure("Z1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + + "Aligned\n" + + "Structure Z1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 6 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2() { + Structure struct = getStructure("Z2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + + "Aligned\n" + + "Structure Z2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3() { + Structure struct = getStructure("Z3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + + "Aligned\n" + + "Structure Z3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 3 longlong:0(7) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4() { + Structure struct = getStructure("Z4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + + "Aligned\n" + + "Structure Z4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 8 longlong:0(7) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5() { + Structure struct = getStructure("Z5"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + + "Aligned\n" + + "Structure Z5 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 longlong:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ6() { + Structure struct = getStructure("Z6"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + + "Aligned\n" + + "Structure Z6 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 longlong:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + " 7 longlong:6(2) 1 e \"\"\n" + + " 8 int:8(0) 1 f \"\"\n" + + " 9 char 1 g \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p1() { + Structure struct = getStructure("B1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + + "Aligned pack(1)\n" + + "Structure B1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p1() { + Structure struct = getStructure("B2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + + "Aligned pack(1)\n" + + "Structure B2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p1() { + Structure struct = getStructure("B3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + + "Aligned pack(1)\n" + + "Structure B3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p1() { + Structure struct = getStructure("Z1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + + "Aligned pack(1)\n" + + "Structure Z1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p1() { + Structure struct = getStructure("Z2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + + "Aligned pack(1)\n" + + "Structure Z2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 5 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1() { + Structure struct = getStructure("Z3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + + "Aligned pack(1)\n" + + "Structure Z3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 3 longlong:0(7) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1T() { + Structure struct = getStructure("Z3p1T"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + + "Aligned\n" + + "Structure Z3p1T {\n" + + " 0 char 1 a \"\"\n" + + " 1 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p1() { + Structure struct = getStructure("Z4p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + + "Aligned pack(1)\n" + + "Structure Z4p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 8 longlong:0(7) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p2() { + Structure struct = getStructure("B1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + + "Aligned pack(2)\n" + + "Structure B1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p2() { + Structure struct = getStructure("B2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + + "Aligned pack(2)\n" + + "Structure B2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p2() { + Structure struct = getStructure("B3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + + "Aligned pack(2)\n" + + "Structure B3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB4p2() { + Structure struct = getStructure("B4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + + "Aligned pack(2)\n" + + "Structure B4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 longlong 8 d \"\"\n" + + " 12 int:4(4) 1 e \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2() { + Structure struct = getStructure("Z1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + + "Aligned pack(2)\n" + + "Structure Z1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2x() { + Structure struct = getStructure("Z1p2x"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + + "Aligned pack(2)\n" + + "Structure Z1p2x {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + " 6 short:4(2) 1 d1 \"\"\n" + + " 6 short:4(6) 2 d2 \"\"\n" + + " 7 short:4(2) 1 d3 \"\"\n" + + " 7 short:4(6) 2 d4 \"\"\n" + + " 8 short:4(2) 1 d5 \"\"\n" + + " 8 short:4(6) 2 d6 \"\"\n" + + " 9 short:4(2) 1 d7 \"\"\n" + + " 10 short:0(7) 1 \"\"\n" + + " 10 ushort:6(2) 1 _b \"\"\n" + + " 10 int:8(2) 2 _c \"\"\n" + + " 11 short:4(6) 2 _d \"\"\n" + + " 12 short:4(2) 1 _d1 \"\"\n" + + " 12 short:4(6) 2 _d2 \"\"\n" + + " 13 short:4(2) 1 _d3 \"\"\n" + + " 13 short:4(6) 2 _d4 \"\"\n" + + " 14 short:4(2) 1 _d5 \"\"\n" + + " 14 short:4(6) 2 _d6 \"\"\n" + + " 15 short:4(2) 1 _d7 \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p2() { + Structure struct = getStructure("Z2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + + "Aligned pack(2)\n" + + "Structure Z2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 int:0(7) 1 \"\"\n" + + " 4 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p2() { + Structure struct = getStructure("Z3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + + "Aligned pack(2)\n" + + "Structure Z3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 3 longlong:0(7) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p2() { + Structure struct = getStructure("Z4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + + "Aligned pack(2)\n" + + "Structure Z4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 8 longlong:0(7) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p2() { + Structure struct = getStructure("Z5p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + + "Aligned pack(2)\n" + + "Structure Z5p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(4) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(7) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p2() { + Structure struct = getStructure("x1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + + "Aligned pack(2)\n" + + "Structure x1p2 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p2() { + Structure struct = getStructure("x2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + + "Aligned pack(2)\n" + + "Structure x2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p2() { + Structure struct = getStructure("x3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + + "Aligned pack(2)\n" + + "Structure x3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(7) 1 \"\"\n" + + " 2 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p2() { + Structure struct = getStructure("x4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + + "Aligned pack(2)\n" + + "Structure x4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + " 4 longlong:0(7) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p4() { + Structure struct = getStructure("Z5p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + + "Aligned pack(4)\n" + + "Structure Z5p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(4) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(7) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p4() { + Structure struct = getStructure("x1p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + + "Aligned pack(4)\n" + + "Structure x1p4 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p4() { + Structure struct = getStructure("x2p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + + "Aligned pack(4)\n" + + "Structure x2p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p4() { + Structure struct = getStructure("x3p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + + "Aligned pack(4)\n" + + "Structure x3p4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(7) 1 \"\"\n" + + " 2 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p4() { + Structure struct = getStructure("x4p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + + "Aligned pack(4)\n" + + "Structure x4p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + " 4 longlong:0(7) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT1() { + Structure struct = getStructure("T1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + + "Aligned\n" + + "Structure T1 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 myEnum:3(5) 1 b \"\"\n" + + " 1 enumTypedef:3(2) 1 c \"\"\n" + + " 2 charTypedef:7(1) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT2() { + Structure struct = getStructure("T2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + + "Aligned\n" + + "Structure T2 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 intTypedef:17(7) 3 b \"\"\n" + + " 3 enumTypedef:3(4) 1 c \"\"\n" + + " 3 charTypedef:3(1) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1() { + Structure struct = getStructure("S1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + + "Aligned\n" + + "Structure S1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p1() { + Structure struct = getStructure("S1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + + "Aligned pack(1)\n" + + "Structure S1p1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p1() { + Structure struct = getStructure("S2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + + "Aligned pack(1)\n" + + "Structure S2p1 {\n" + + " 0 B1p1 4 b1p1 \"\"\n" + + " 4 B2p1 4 b2p1 \"\"\n" + + " 8 Z1p1 7 z1p1 \"\"\n" + + " 15 Z2p1 5 z2p1 \"\"\n" + + " 20 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 28 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p2() { + Structure struct = getStructure("S1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + + "Aligned pack(2)\n" + + "Structure S1p2 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p2() { + Structure struct = getStructure("S2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + + "Aligned pack(2)\n" + + "Structure S2p2 {\n" + + " 0 B1p2 4 b1p2 \"\"\n" + + " 4 B2p2 4 b2p2 \"\"\n" + + " 8 Z1p2 8 z1p2 \"\"\n" + + " 16 Z2p2 6 z2p2 \"\"\n" + + " 22 Z3p2 8 z3p2 \"\"\n" + + "}\n" + + "Size = 30 Actual Alignment = 2", struct); + //@formatter:on + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java new file mode 100644 index 0000000000..c01550eae3 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java @@ -0,0 +1,816 @@ +/* ### + * 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.program.model.data; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Iterator; + +import org.junit.Test; + +import resources.ResourceManager; + +public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { + + // NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2) + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + super.setUp(); + + // uncomment to generate datatype archive + // writeArchive(); + } + + private void writeArchive() throws IOException { + URL resource = ResourceManager.getResource(C_SOURCE_FILE); + File f = new File(resource.getPath() + ".gdt"); + if (f.exists()) { + f.delete(); + } + + FileDataTypeManager fileDtMgr = FileDataTypeManager.createFileArchive(f); + int txId = fileDtMgr.startTransaction("Save Datatypes"); + try { + Iterator composites = getDataTypeManager().getAllComposites(); + while (composites.hasNext()) { + fileDtMgr.addDataType(composites.next(), null); + } + } + finally { + fileDtMgr.endTransaction(txId, true); + } + + fileDtMgr.save(); + fileDtMgr.close(); + } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureImplBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); + dataMgr = new MyDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureBitFieldsB1() { + Structure struct = getStructure("B1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + + "Aligned\n" + + "Structure B1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2() { + Structure struct = getStructure("B2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + + "Aligned\n" + + "Structure B2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3() { + Structure struct = getStructure("B3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + + "Aligned\n" + + "Structure B3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + // gcc groups with previous non-bitfield + " 1 int:8(6) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1() { + Structure struct = getStructure("Z1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + + "Aligned\n" + + "Structure Z1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 6 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2() { + Structure struct = getStructure("Z2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + + "Aligned\n" + + "Structure Z2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3() { + Structure struct = getStructure("Z3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + + "Aligned\n" + + "Structure Z3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 3 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4() { + Structure struct = getStructure("Z4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + + "Aligned\n" + + "Structure Z4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5() { + Structure struct = getStructure("Z5"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + + "Aligned\n" + + "Structure Z5 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 longlong:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ6() { + Structure struct = getStructure("Z6"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + + "Aligned\n" + + "Structure Z6 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 longlong:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + " 7 longlong:6(0) 1 e \"\"\n" + + " 8 int:8(0) 1 f \"\"\n" + + " 9 char 1 g \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p1() { + Structure struct = getStructure("B1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + + "Aligned pack(1)\n" + + "Structure B1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p1() { + Structure struct = getStructure("B2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + + "Aligned pack(1)\n" + + "Structure B2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p1() { + Structure struct = getStructure("B3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + + "Aligned pack(1)\n" + + "Structure B3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p1() { + Structure struct = getStructure("Z1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + + "Aligned pack(1)\n" + + "Structure Z1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p1() { + Structure struct = getStructure("Z2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + + "Aligned pack(1)\n" + + "Structure Z2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 5 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1() { + Structure struct = getStructure("Z3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + + "Aligned pack(1)\n" + + "Structure Z3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 3 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1T() { + Structure struct = getStructure("Z3p1T"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + + "Aligned\n" + + "Structure Z3p1T {\n" + + " 0 char 1 a \"\"\n" + + " 1 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p1() { + Structure struct = getStructure("Z4p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + + "Aligned pack(1)\n" + + "Structure Z4p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p2() { + Structure struct = getStructure("B1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + + "Aligned pack(2)\n" + + "Structure B1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p2() { + Structure struct = getStructure("B2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + + "Aligned pack(2)\n" + + "Structure B2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p2() { + Structure struct = getStructure("B3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + + "Aligned pack(2)\n" + + "Structure B3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB4p2() { + Structure struct = getStructure("B4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + + "Aligned pack(2)\n" + + "Structure B4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 longlong 8 d \"\"\n" + + " 12 int:4(0) 1 e \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2() { + Structure struct = getStructure("Z1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + + "Aligned pack(2)\n" + + "Structure Z1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2x() { + Structure struct = getStructure("Z1p2x"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + + "Aligned pack(2)\n" + + "Structure Z1p2x {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + " 6 short:4(2) 1 d1 \"\"\n" + + " 6 short:4(6) 2 d2 \"\"\n" + + " 7 short:4(2) 1 d3 \"\"\n" + + " 7 short:4(6) 2 d4 \"\"\n" + + " 8 short:4(2) 1 d5 \"\"\n" + + " 8 short:4(6) 2 d6 \"\"\n" + + " 9 short:4(2) 1 d7 \"\"\n" + + " 10 short:0(0) 1 \"\"\n" + + " 10 ushort:6(0) 1 _b \"\"\n" + + " 10 int:8(6) 2 _c \"\"\n" + + " 11 short:4(6) 2 _d \"\"\n" + + " 12 short:4(2) 1 _d1 \"\"\n" + + " 12 short:4(6) 2 _d2 \"\"\n" + + " 13 short:4(2) 1 _d3 \"\"\n" + + " 13 short:4(6) 2 _d4 \"\"\n" + + " 14 short:4(2) 1 _d5 \"\"\n" + + " 14 short:4(6) 2 _d6 \"\"\n" + + " 15 short:4(2) 1 _d7 \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p2() { + Structure struct = getStructure("Z2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + + "Aligned pack(2)\n" + + "Structure Z2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 int:0(0) 1 \"\"\n" + + " 4 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p2() { + Structure struct = getStructure("Z3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + + "Aligned pack(2)\n" + + "Structure Z3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 3 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p2() { + Structure struct = getStructure("Z4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + + "Aligned pack(2)\n" + + "Structure Z4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p2() { + Structure struct = getStructure("Z5p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + + "Aligned pack(2)\n" + + "Structure Z5p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(0) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p2() { + Structure struct = getStructure("x1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + + "Aligned pack(2)\n" + + "Structure x1p2 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p2() { + Structure struct = getStructure("x2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + + "Aligned pack(2)\n" + + "Structure x2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p2() { + Structure struct = getStructure("x3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + + "Aligned pack(2)\n" + + "Structure x3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(0) 1 \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p2() { + Structure struct = getStructure("x4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + + "Aligned pack(2)\n" + + "Structure x4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + " 4 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p4() { + Structure struct = getStructure("Z5p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + + "Aligned pack(4)\n" + + "Structure Z5p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(0) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(0) 1 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p4() { + Structure struct = getStructure("x1p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + + "Aligned pack(4)\n" + + "Structure x1p4 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p4() { + Structure struct = getStructure("x2p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + + "Aligned pack(4)\n" + + "Structure x2p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p4() { + Structure struct = getStructure("x3p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + + "Aligned pack(4)\n" + + "Structure x3p4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(0) 1 \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p4() { + Structure struct = getStructure("x4p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + + "Aligned pack(4)\n" + + "Structure x4p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + " 4 longlong:0(0) 1 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT1() { + Structure struct = getStructure("T1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + + "Aligned\n" + + "Structure T1 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 myEnum:3(0) 1 b \"\"\n" + + " 1 enumTypedef:3(3) 1 c \"\"\n" + + " 2 charTypedef:7(0) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT2() { + Structure struct = getStructure("T2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + + "Aligned\n" + + "Structure T2 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 intTypedef:17(0) 3 b \"\"\n" + + " 3 enumTypedef:3(1) 1 c \"\"\n" + + " 3 charTypedef:3(4) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1() { + Structure struct = getStructure("S1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + + "Aligned\n" + + "Structure S1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p1() { + Structure struct = getStructure("S1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + + "Aligned pack(1)\n" + + "Structure S1p1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p1() { + Structure struct = getStructure("S2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + + "Aligned pack(1)\n" + + "Structure S2p1 {\n" + + " 0 B1p1 4 b1p1 \"\"\n" + + " 4 B2p1 4 b2p1 \"\"\n" + + " 8 Z1p1 7 z1p1 \"\"\n" + + " 15 Z2p1 5 z2p1 \"\"\n" + + " 20 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 28 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p2() { + Structure struct = getStructure("S1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + + "Aligned pack(2)\n" + + "Structure S1p2 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p2() { + Structure struct = getStructure("S2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + + "Aligned pack(2)\n" + + "Structure S2p2 {\n" + + " 0 B1p2 4 b1p2 \"\"\n" + + " 4 B2p2 4 b2p2 \"\"\n" + + " 8 Z1p2 8 z1p2 \"\"\n" + + " 16 Z2p2 6 z2p2 \"\"\n" + + " 22 Z3p2 8 z3p2 \"\"\n" + + "}\n" + + "Size = 30 Actual Alignment = 2", struct); + //@formatter:on + } + +// @Test +// public void testStructureBitFieldsFOO() { +// Structure struct = getStructure("Z3p1T"); +// +// System.out.println(struct.toString()); +// +// DataTypeManager dtm = struct.getDataTypeManager(); +// if (dtm instanceof StandAloneDataTypeManager) { +// ((StandAloneDataTypeManager) dtm).startTransaction("TEST"); +// } +// else if (dtm instanceof ProgramDataTypeManager) { +// ((ProgramDataTypeManager) dtm).getProgram().startTransaction("TEST"); +// } +// +// ArrayList components = +// (ArrayList) getInstanceField("components", struct); +// +// AlignedStructurePacker.packComponents(struct, components); +// +// //@formatter:off +// CompositeTestUtils.assertExpectedComposite(this, "", struct); +// //@formatter:on +// } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java new file mode 100644 index 0000000000..9a3442cc6c --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java @@ -0,0 +1,109 @@ +/* ### + * 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.program.model.data; + +import org.junit.Test; + +public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { + + // NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2 + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureImplBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + dataMgr = new MyDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testUnionBitFieldsU1() { + Union struct = getUnion("U1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + + "Aligned\n" + + "Union U1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1z() { + Union struct = getUnion("U1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + + "Aligned\n" + + "Union U1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 1 \"\"\n" + // has no impact + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1() { + Union struct = getUnion("U1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + + "Aligned pack(1)\n" + + "Union U1p1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1z() { + Union struct = getUnion("U1p1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + + "Aligned pack(1)\n" + + "Union U1p1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 1 \"\"\n" + // has no impact + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p2() { + Union struct = getUnion("U1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + + "Aligned pack(2)\n" + + "Union U1p2 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 2 Actual Alignment = 2", struct); + //@formatter:on + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java new file mode 100644 index 0000000000..26122e850f --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java @@ -0,0 +1,109 @@ +/* ### + * 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.program.model.data; + +import org.junit.Test; + +public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { + + // NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2) + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureImplBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); + dataMgr = new MyDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testUnionBitFieldsU1() { + Union struct = getUnion("U1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + + "Aligned\n" + + "Union U1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1z() { + Union struct = getUnion("U1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + + "Aligned\n" + + "Union U1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 1 \"\"\n" + // has no impact + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1() { + Union struct = getUnion("U1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + + "Aligned pack(1)\n" + + "Union U1p1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1z() { + Union struct = getUnion("U1p1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + + "Aligned pack(1)\n" + + "Union U1p1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 1 \"\"\n" + // has no impact + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p2() { + Union struct = getUnion("U1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + + "Aligned pack(2)\n" + + "Union U1p2 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 2 Actual Alignment = 2", struct); + //@formatter:on + } +} diff --git a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/bitfields.c b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/bitfields.c new file mode 100644 index 0000000000..a8e3789cb5 --- /dev/null +++ b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/bitfields.c @@ -0,0 +1,184 @@ +/* ### + * 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. + */ +#include +#include "bitfields.h" + + +struct B1 B1 = { 5, 0x2A, -1, 0xA }; + +struct B2 B2 = { 5, 0x2A, -1, 0xA }; + +struct B3 B3 = { 5, 0x2A, -1, 0xA }; + + +struct Z1 Z1 = { 5, 0x2A, -1, 0xA }; + +struct Z2 Z2 = { 5, 0x2A, -1, 0xA }; + +struct Z3 Z3 = { 5, 0x2A, -1, 0xA }; + +struct Z4 Z4 = { 5, 0x2A, -1, 0xA }; + +struct Z5 Z5 = { 5, 0x2A, -1, 0xA }; + +struct Z6 Z6 = { 5, 0x2A, -1, 0xA, 0x2A, -1, 0xA }; + + +struct B1p1 B1p1 = { 5, 0x2A, -1, 0xA }; + +struct B2p1 B2p1 = { 5, 0x2A, -1, 0xA }; + +struct B3p1 B3p1 = { 5, 0x2A, -1, 0xA }; + + +struct Z1p1 Z1p1 = { 5, 0x2A, -1, 0xA }; + +struct Z2p1 Z2p1 = { 5, 0x2A, -1, 0xA }; + +struct Z3p1 Z3p1 = { 5, 0x2A, -1, 0xA }; +struct Z3p1T Z3p1T = { 7, { 5, 0x2A, -1, 0xA }}; + +struct Z4p1 Z4p1 = { 5, 0x2A, -1, 0xA }; + + +struct B1p2 B1p2 = { 5, 0x2A, -1, 0xA }; + +struct B2p2 B2p2 = { 5, 0x2A, -1, 0xA }; + +struct B3p2 B3p2 = { 5, 0x2A, -1, 0xA }; + +struct B4p2 B4p2 = { 5, 0x2A, -1, 0x5555555555555555, 0xA }; + + +struct Z1p2 Z1p2 = { 5, 0x2A, -1, 0xA }; + +struct Z1p2x Z1p2x = { 5, 0x2A, -1, 0xA, -1, 0, -1, 0, -1, 0, -1, 0x2A, -1, 0xA, -1, 0, -1, 0, -1, 0, -1 }; + +struct Z2p2 Z2p2 = { 5, 0x2A, -1, 0xA }; + +struct Z3p2 Z3p2 = { 5, 0x2A, -1, 0xA }; + +struct Z4p2 Z4p2 = { 5, 0x2A, -1, 0xA }; + +struct Z5p2 Z5p2 = { 5, 0x2A, -1, 0xA }; + +struct x1p2 x1p2 = { 5 }; + +struct x2p2 x2p2 = { 5, 0x2A }; + +struct x3p2 x3p2 = { 5, 0x2A }; + +struct x4p2 x4p2 = { 5, 0x2A }; + + +struct Z5p4 Z5p4 = { 5, 0x2A, -1, 0xA }; + +struct x1p4 x1p4 = { 5 }; + +struct x2p4 x2p4 = { 5, 0x2A }; + +struct x3p4 x3p4 = { 5, 0x2A }; + +struct x4p4 x4p4 = { 5, 0x2A }; + + +struct S1 S1 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } }; + + +struct S1p1 S1p1 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } }; + +struct S2p1 S2p1 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } }; + + +struct S1p2 S1p2 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } }; + +struct S2p2 S2p2 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } }; + +struct T1 T1 = { 5, TWO, THREE, 1 }; + +struct T2 T2 = { 5, 0x2A, THREE, 1 }; + +union U1 U1; +union U1z U1z; +union U1p1 U1p1; +union U1p1z U1p1z; +union U1p2 U1p2; + +struct SUp1 SUp1; + +int main(int argc, char *argv[]) { + printf("len B1: %d\n", sizeof(struct B1)); + printf("len B2: %d\n", sizeof(struct B2)); + printf("len B3: %d\n", sizeof(struct B3)); + + printf("len Z1: %d\n", sizeof(struct Z1)); + printf("len Z2: %d\n", sizeof(struct Z2)); + printf("len Z3: %d\n", sizeof(struct Z3)); + printf("len Z4: %d\n", sizeof(struct Z4)); + printf("len Z5: %d\n", sizeof(struct Z5)); + printf("len Z6: %d\n", sizeof(struct Z6)); + + printf("len B1p1: %d\n", sizeof(struct B1p1)); + printf("len B2p1: %d\n", sizeof(struct B2p1)); + printf("len B3p1: %d\n", sizeof(struct B3p1)); + + printf("len Z1p1: %d\n", sizeof(struct Z1p1)); + printf("len Z2p1: %d\n", sizeof(struct Z2p1)); + printf("len Z3p1: %d\n", sizeof(struct Z3p1)); + printf("len Z3p1T: %d\n", sizeof(struct Z3p1T)); + printf("len Z4p1: %d\n", sizeof(struct Z4p1)); + + printf("len B1p2: %d\n", sizeof(struct B1p2)); + printf("len B2p2: %d\n", sizeof(struct B2p2)); + printf("len B3p2: %d\n", sizeof(struct B3p2)); + printf("len B4p2: %d\n", sizeof(struct B4p2)); + + printf("len Z1p2: %d\n", sizeof(struct Z1p2)); + printf("len Z1p2x: %d\n", sizeof(struct Z1p2x)); + printf("len Z2p2: %d\n", sizeof(struct Z2p2)); + printf("len Z3p2: %d\n", sizeof(struct Z3p2)); + printf("len Z4p2: %d\n", sizeof(struct Z4p2)); + printf("len Z5p2: %d\n", sizeof(struct Z5p2)); + printf("len x1p2: %d\n", sizeof(struct x1p2)); + printf("len x2p2: %d\n", sizeof(struct x2p2)); + printf("len x3p2: %d\n", sizeof(struct x3p2)); + printf("len x4p2: %d\n", sizeof(struct x4p2)); + + printf("len Z5p4: %d\n", sizeof(struct Z5p4)); + printf("len x1p4: %d\n", sizeof(struct x1p4)); + printf("len x2p4: %d\n", sizeof(struct x2p4)); + printf("len x3p4: %d\n", sizeof(struct x3p4)); + printf("len x4p4: %d\n", sizeof(struct x4p4)); + + printf("len S1: %d\n", sizeof(struct S1)); + + printf("len S1p1: %d\n", sizeof(struct S1p1)); + printf("len S2p1: %d\n", sizeof(struct S2p1)); + + printf("len S1p2: %d\n", sizeof(struct S1p2)); + printf("len S2p2: %d\n", sizeof(struct S2p2)); + + printf("len T1: %d\n", sizeof(struct T1)); + printf("len T2: %d\n", sizeof(struct T2)); + + printf("len U1: %d\n", sizeof(union U1)); + printf("len U1z: %d\n", sizeof(union U1z)); + printf("len U1p1: %d\n", sizeof(union U1p1)); + printf("len U1p1z: %d\n", sizeof(union U1p1z)); + printf("len U1p2: %d\n", sizeof(union U1p2)); + + printf("len SUp1: %d\n", sizeof(struct SUp1)); +} diff --git a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/bitfields.h b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/bitfields.h new file mode 100644 index 0000000000..5b4acfa710 --- /dev/null +++ b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/bitfields.h @@ -0,0 +1,445 @@ +/* ### + * 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. + */ +// Verify bitfield grouping and alignment without zero-length bitfields + +struct B1 { + char a; + unsigned short b:6; // gcc groups with previous non-bitfield + int c:8; // gcc groups with previous two fields (including non-bitfield) + short d:4; +}; + +struct B2 { + char a; + unsigned short b:6; // gcc groups with previous non-bitfield + int c:8; + int d:4; +}; + +struct B3 { + char a; + unsigned short b:6; // gcc groups with previous non-bitfield + int c:8; + char d; // gcc groups with int bit-field +}; + + + +// Verify bitfield grouping and alignment with zero-length bitfields + +struct Z1 { + char a; + int :0; // MSVC ignores field, gcc forces break and does not combine with previous field + unsigned short b:6; + int c:8; + short d:4; +}; + +struct Z2 { + char a; + unsigned short b:6; // gcc groups with previous non-bitfield + int c:8; + int :0; + short d:4; +}; + +struct Z3 { + char a; + unsigned short b:6; // gcc groups with previous non-bitfield + int c:8; // gcc groups with previous two fields (including non-bitfield) + int d:4; + long long :0; // trailing :0 imposes alignment onto structure +}; + +struct Z4 { + char a; + unsigned short b:6; // gcc groups with previous non-bitfield + int c:8; // gcc groups with previous two fields (including non-bitfield) + long long :0; // forced alignment of non-bitfield + char d; +}; + +struct Z5 { + char a; + int :0; + long long b:6; + int c:8; + char d; +}; + +struct Z6 { + char a; + int :0; + long long b:6; + int c:8; + char d; + long long e:6; + int f:8; + char g; +}; + +#pragma pack(1) + +// Verify bitfield grouping and alignment without zero-length bitfields + +struct B1p1 { + char a; + unsigned short b:6; + int c:8; + short d:4; +}; + +struct B2p1 { + char a; + unsigned short b:6; + int c:8; + int d:4; +}; + +struct B3p1 { + char a; + unsigned short b:6; + int c:8; + char d; // gcc groups with int bit-field +}; + + + +// Verify bitfield grouping and alignment with zero-length bitfields + +struct Z1p1 { + char a; + int :0; // MSVC ignores field + unsigned short b:6; + int c:8; + short d:4; +}; + +struct Z2p1 { + char a; + unsigned short b:6; + int c:8; + int :0; + short d:4; +}; + +struct Z3p1 { + char a; + unsigned short b:6; + int c:8; + int d:4; + long long :0; // trailing :0 (ignore when packing ?) - case needs more testing +}; + +struct Z4p1 { + char a; + unsigned short b:6; + int c:8; + long long :0; // forced alignment of non-bitfield + char d; +}; + + +#pragma pack() + +// packed structure contained within default aligned structure +struct Z3p1T { + char a; + struct Z3p1 z3p1; +}; + +#pragma pack(2) + + +// Verify bitfield grouping and alignment without zero-length bitfields + +struct B1p2 { + char a; + unsigned short b:6; + int c:8; + short d:4; +}; + +struct B2p2 { + char a; + unsigned short b:6; + int c:8; + int d:4; +}; + +struct B3p2 { + char a; + unsigned short b:6; + int c:8; + char d; // gcc groups with int bit-field +}; + +struct B4p2 { + char a; + unsigned short b:6; + int c:8; + long long d; + int e:4; +}; + + + +// Verify bitfield grouping and alignment with zero-length bitfields + +struct Z1p2 { + char a; + int :0; // MSVC ignores field + unsigned short b:6; + int c:8; + short d:4; // NOTE: gcc appears ignore short alignment constraint due to int :0 ??? +}; + +struct Z1p2x { + char a; + int :0; // MSVC ignores field + unsigned short b:6; + int c:8; + short d:4; // NOTE: gcc appears ignore short alignment constraint due to int :0 ??? + short d1:4; + short d2:4; + short d3:4; + short d4:4; + short d5:4; + short d6:4; + short d7:4; + + short :0; + unsigned short _b:6; + int _c:8; + short _d:4; // NOTE: gcc appears ignore short alignment constraint due to int :0 ??? + short _d1:4; + short _d2:4; + short _d3:4; + short _d4:4; + short _d5:4; + short _d6:4; + short _d7:4; + +}; + +struct Z2p2 { + char a; + unsigned short b:6; + int c:8; + int :0; + short d:4; +}; + +struct Z3p2 { + char a; + unsigned short b:6; + int c:8; + int d:4; + long long :0; // trailing :0 (ignore when packing ?) - case needs more testing +}; + +struct Z4p2 { + char a; + unsigned short b:6; + int c:8; + long long :0; // forced alignment of non-bitfield + char d; +}; + +struct Z5p2 { + char a; + unsigned short b:12; + int c:8; + long long :0; // forced alignment of non-bitfield + char d; +}; + +struct x1p2 { + char a; +}; + +struct x2p2 { + char a; + int b:27; +}; + +struct x3p2 { + char a; + short :0; + int b:27; +}; + +struct x4p2 { + char a; + int b:27; + long long :0; +}; + + +#pragma pack() + +#pragma pack(4) + +struct Z5p4 { + char a; + unsigned short b:12; + int c:8; + long long :0; // forced alignment of non-bitfield + char d; +}; + +struct x1p4 { + char a; +}; + +struct x2p4 { + char a; + int b:27; +}; + +struct x3p4 { + char a; + short :0; + int b:27; +}; + +struct x4p4 { + char a; + int b:27; + long long :0; +}; + +#pragma pack() + + +// Structures within structures + +struct S1 { + struct B1 b1; + struct B2 b2; + struct Z1 z1; + struct Z2 z2; + struct Z3 z3; +}; + + +#pragma pack(1) + +struct S1p1 { + struct B1 b1; + struct B2 b2; + struct Z1 z1; + struct Z2 z2; + struct Z3 z3; +}; + +struct S2p1 { + struct B1p1 b1p1; + struct B2p1 b2p1; + struct Z1p1 z1p1; + struct Z2p1 z2p1; + struct Z3p1 z3p1; +}; + +#pragma pack() + + +#pragma pack(2) + +struct S1p2 { + struct B1 b1; + struct B2 b2; + struct Z1 z1; + struct Z2 z2; + struct Z3 z3; +}; + +struct S2p2 { + struct B1p2 b1p2; + struct B2p2 b2p2; + struct Z1p2 z1p2; + struct Z2p2 z2p2; + struct Z3p2 z3p2; +}; + +#pragma pack() + +enum myEnum { ONE, TWO, THREE }; + +typedef enum myEnum enumTypedef; + +typedef int intTypedef; + +typedef char charTypedef; + +typedef short shortTypedef; + +struct T1 { + charTypedef a; + enum myEnum b:3; + enumTypedef c:3; + charTypedef d:7; +}; + +struct T2 { + charTypedef a; + intTypedef b:17; + enumTypedef c:3; + charTypedef d:3; +}; + +// Unions + +union U1 { + int a:4; + int b:2; +}; + +union U1z { + int a:4; + long long :0; + int b:2; +}; + +#pragma pack(1) + +union U1p1 { + int a:4; + int b:2; +}; + +union U1p1z { + int a:4; + long long :0; + int b:2; +}; + +struct SUp1 { + char a; + union U1p1z u; +}; + +#pragma pack(2) + +union U1p2 { + int a:4; + int b:2; +}; + +#pragma pack() + + + diff --git a/Ghidra/Features/PDB/build.gradle b/Ghidra/Features/PDB/build.gradle index f435f6a20a..6b005aa38b 100644 --- a/Ghidra/Features/PDB/build.gradle +++ b/Ghidra/Features/PDB/build.gradle @@ -15,6 +15,12 @@ dependencies { compile project(":Base") testCompile "org.jmockit:jmockit:1.44" + + // Demangler Analyzer needs to find MicrosoftDemangler + compile project(":MicrosoftDemangler") + + testCompile project(path: ':Base', configuration: 'testArtifacts') + testCompile project(path: ':SoftwareModeling', configuration: 'testArtifacts') } /** diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java index ba7ba060e8..89b1c79770 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java @@ -130,14 +130,14 @@ public class PdbAnalyzer extends AbstractAnalyzer { boolean parsePdb(File pdb, Program program, AutoAnalysisManager mgr, TaskMonitor monitor, MessageLog log) { DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService(); - PdbParserNEW parser = new PdbParserNEW(pdb, program, dataTypeManagerService, true); + PdbParserNEW parser = new PdbParserNEW(pdb, program, dataTypeManagerService, true, monitor); String message; try { parser.parse(); parser.openDataTypeArchives(); - parser.applyTo(monitor, log); + parser.applyTo(log); return true; } catch (PdbException e) { @@ -150,7 +150,11 @@ public class PdbAnalyzer extends AbstractAnalyzer { return false; } catch (Exception e) { - Msg.showError(this, null, ERROR_TITLE, e.getMessage(), e); + String msg = e.getMessage(); + if (msg == null) { + msg = e.toString(); + } + Msg.showError(this, null, ERROR_TITLE, msg, e); return false; } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyDataTypes.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyDataTypes.java index ed24a299bd..1451506c28 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyDataTypes.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyDataTypes.java @@ -19,46 +19,76 @@ import java.util.*; import org.xml.sax.SAXParseException; +import ghidra.app.util.bin.format.pdb.PdbParserNEW.PdbXmlMember; import ghidra.app.util.importer.MessageLog; -import ghidra.program.model.data.*; +import ghidra.graph.*; +import ghidra.graph.algo.GraphNavigator; +import ghidra.graph.jung.JungDirectedGraph; +import ghidra.program.model.data.Composite; +import ghidra.program.model.data.DataType; import ghidra.program.model.symbol.SymbolUtilities; +import ghidra.util.Msg; +import ghidra.util.SystemUtilities; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; import ghidra.util.xml.XmlUtilities; -import ghidra.xml.*; +import ghidra.xml.XmlElement; +import ghidra.xml.XmlPullParser; public class ApplyDataTypes { private PdbParserNEW pdbParser; - private boolean isClasses; private MessageLog log; - private List todo = new ArrayList<>(); + private HashMap compositeQueue = new HashMap<>(); /** - * Construct a PDB XML datatype or class parser. This will pre-process each datatype element and cache - * a properly sized composite for subsequent type reference. The full parse will not be completed - * until the {@link #applyTo(TaskMonitor)} method is invoked after all types and classes have been - * pre-processed or applied. + * Construct a PDB XML datatype or class parser. The {@link #preProcessDataTypeList(XmlPullParser, boolean, TaskMonitor)} + * method must be used to injest member elements from the pull parser to populate the set of type to be parsed. + * The full parse will not be completed until the {@link #applyTo(TaskMonitor)} method is invoked after all types + * and classes have been pre-processed or applied. * @param pdbParser PDB parser object * @param xmlParser XML parser positioned immediately after datatypes or classes element * @param isClasses true if processing classes, false if composite datatypes - * @param monitor task progress monitor * @param log message log used during construction and subsequent method invocations * @throws CancelledException if monitor is cancelled * @throws SAXParseException PDB XML parse failure */ - ApplyDataTypes(PdbParserNEW pdbParser, XmlPullParser xmlParser, boolean isClasses, - TaskMonitor monitor, MessageLog log) throws CancelledException, SAXParseException { + ApplyDataTypes(PdbParserNEW pdbParser, MessageLog log) + throws CancelledException, SAXParseException { this.pdbParser = pdbParser; - this.isClasses = isClasses; this.log = log; - - // Build todo list and cache preliminary composite definitions - preProcessDataTypeList(xmlParser, monitor); } void dispose() { - todo.clear(); + compositeQueue.clear(); + } + + private List getCompositeDefinitionsInpostDependencyOrder( + TaskMonitor monitor) { + + JungDirectedGraph> graph = + new JungDirectedGraph<>(); + for (CompositeDefinition compositeDefinition : compositeQueue.values()) { + graph.addVertex(compositeDefinition); + for (PdbMember m : compositeDefinition.memberList) { + String name = m.memberDataTypeName; + int index = name.indexOf('['); + if (index > 0) { + name = name.substring(0, index).trim(); + } + CompositeDefinition child = compositeQueue.get(name); + if (child != null) { + graph.addEdge(new DefaultGEdge<>(compositeDefinition, child)); + } + } + } + +// FIXME: GraphAlgorithms.findCircuits(graph, monitor); + + List verticesInPostOrder = + GraphAlgorithms.getVerticesInPostOrder(graph, GraphNavigator.topDownNavigator()); + + return verticesInPostOrder; } /** @@ -68,75 +98,67 @@ public class ApplyDataTypes { */ void buildDataTypes(TaskMonitor monitor) throws CancelledException { + monitor.setMessage("Order PDB datatypes... "); + + List verticesInPostOrder = + getCompositeDefinitionsInpostDependencyOrder(monitor); + monitor.setMessage("Building PDB datatypes... "); - for (XmlTreeNode node : todo) { + for (CompositeDefinition compositeDefinition : verticesInPostOrder) { monitor.checkCanceled(); - XmlElement elem = node.getStartElement(); - String name = SymbolUtilities.replaceInvalidChars(elem.getAttribute("name"), false); - - String kind = isClasses ? PdbParserNEW.STRUCTURE_KIND : elem.getAttribute("kind"); - int length = XmlUtilities.parseInt(elem.getAttribute("length")); - // namespace qualified name used for cache lookups - DataType cachedDataType = pdbParser.getCachedDataType(name); + DataType cachedDataType = pdbParser.getCachedDataType(compositeDefinition.name); if (!(cachedDataType instanceof Composite) || - !cachedDataType.getCategoryPath().equals(pdbParser.getCategory(name, true)) || - !pdbParser.isCorrectKind(cachedDataType, kind)) { - log.appendMsg("Error: Conflicting data type name: " + name); + !cachedDataType.getCategoryPath().equals( + pdbParser.getCategory(compositeDefinition.name, true)) || + !pdbParser.isCorrectKind(cachedDataType, compositeDefinition.kind)) { + log.appendMsg("Error: Conflicting data type name: " + compositeDefinition.name); continue; } Composite composite = (Composite) cachedDataType; PdbUtil.clearComponents(composite); - if (!CompositeMember.applyDataTypeMembers(pdbParser, composite, length, node, - monitor)) { + if (!DefaultCompositeMember.applyDataTypeMembers(composite, compositeDefinition.isClass, + compositeDefinition.length, getNormalMembersOnly(compositeDefinition), + msg -> Msg.warn(this, msg), monitor)) { PdbUtil.clearComponents(composite); } - // Do not adjust size of defined structure contains flex array at specified offset - boolean hasFlexibleArray = false; - if (composite instanceof Structure) { - hasFlexibleArray = ((Structure) composite).hasFlexibleArrayComponent(); - } +// // Do not adjust size of defined structure contains flex array at specified offset +// boolean hasFlexibleArray = false; +// if (composite instanceof Structure) { +// hasFlexibleArray = ((Structure) composite).hasFlexibleArrayComponent(); +// } - if (!isClasses && !hasFlexibleArray) { - PdbUtil.ensureSize(length, composite, log); - } +// FIXME: This should be handled during finalization of composite layout +// if (!isClass && !hasFlexibleArray) { +// PdbUtil.ensureSize(length, composite, log); +// } } } - /** - * check to see if this data type is actually a class - */ - private boolean isDataTypeClass(XmlTreeNode node, TaskMonitor monitor) - throws CancelledException { - if (!node.getStartElement().getName().equals("datatype")) { - return false; + private List getNormalMembersOnly(CompositeDefinition compositeDefinition) { + if (compositeDefinition.hasNormalMembersOnly) { + return compositeDefinition.memberList; } - for (int i = 0; i < node.getChildCount(); ++i) { - if (monitor.isCancelled()) { - throw new CancelledException(); - } - XmlTreeNode childNode = node.getChildAt(i); - XmlElement child = childNode.getStartElement(); - String datatype = child.getAttribute("datatype"); - if ("Function".equals(datatype)) { - return true; + ArrayList list = new ArrayList<>(); + for (PdbXmlMember m : compositeDefinition.memberList) { + if (m.kind == PdbXmlKind.MEMBER) { + list.add(m); } } - return false; + return list; } - private void preProcessDataTypeList(XmlPullParser xmlParser, TaskMonitor monitor) + void preProcessDataTypeList(XmlPullParser xmlParser, boolean isClasses, TaskMonitor monitor) throws SAXParseException, CancelledException { monitor.setMessage("Pre-processing PDB datatypes..."); String elementType = isClasses ? "classes" : "datatypes"; - Map todoNames = new HashMap<>(); while (xmlParser.hasNext()) { monitor.checkCanceled(); XmlElement elem = xmlParser.peek(); @@ -144,50 +166,106 @@ public class ApplyDataTypes { xmlParser.next(); break; } - String name = SymbolUtilities.replaceInvalidChars(elem.getAttribute("name"), false); - XmlTreeNode node = new XmlTreeNode(xmlParser); - if (todoNames.containsKey(name)) { - XmlTreeNode todoNode = todoNames.get(name); - if (elem.toString().equals(todoNode.getStartElement().toString())) { - //TODO log.appendMsg("Duplicate data type defined in PDB: "+name); - } - else { - //TODO log.appendMsg("Data type re-definition ignored: "+name); - } - } - else { - if (isClasses || isDataTypeClass(node, monitor)) { - pdbParser.predefineClass(name); - } - todoNames.put(name, node); - String kind = isClasses ? PdbParserNEW.STRUCTURE_KIND : elem.getAttribute("kind"); + CompositeDefinition compositeDefinition = new CompositeDefinition(xmlParser); - if (pdbParser.getCachedDataType(name) != null) { - log.appendMsg( - "Error: Data type name collision - unable to define " + kind + ": " + name); + if (!compositeQueue.containsKey(compositeDefinition.name)) { + // could be problematic if duplicate names represent two different composites + if (compositeDefinition.isClass) { + pdbParser.predefineClass(compositeDefinition.name); + } + compositeQueue.put(compositeDefinition.name, compositeDefinition); + + if (pdbParser.getCachedDataType(compositeDefinition.name) != null) { + log.appendMsg("Error: Data type name collision - unable to define " + + compositeDefinition.kind.getCamelName() + ": " + compositeDefinition.name); continue; } - todo.add(node); - +// /** Can this be avoided if using dependency ordering ?? // NOTE: currently composite may grow if zero-length array used // since we must currently allocate one element since 0-length array // not yet supported. - Composite composite = pdbParser.createComposite(kind, name); + Composite composite = + pdbParser.createComposite(compositeDefinition.kind, compositeDefinition.name); if (composite == null) { - log.appendMsg("Unsupported datatype kind (" + kind + "): " + name); + log.appendMsg("Unsupported datatype kind (" + compositeDefinition.kind + "): " + + compositeDefinition.name); continue; } - if (!isClasses) { - int length = XmlUtilities.parseInt(elem.getAttribute("length")); - PdbUtil.ensureSize(length, composite, log); - } - pdbParser.cacheDataType(name, composite); +// if (!isClasses) { +// int length = XmlUtilities.parseInt(elem.getAttribute("length")); +// PdbUtil.ensureSize(length, composite, log); +// } + pdbParser.cacheDataType(compositeDefinition.name, composite); +// **/ } } - todoNames.clear();//release memory... - todoNames = null; + } + + private class CompositeDefinition { + final boolean isClass; + final PdbXmlKind kind; + final String name; + final int length; + final List memberList = new ArrayList<>(); + final boolean hasNormalMembersOnly; + + CompositeDefinition(XmlPullParser parser) { + XmlElement startElement = parser.start(); + name = SymbolUtilities.replaceInvalidChars(startElement.getAttribute("name"), false); + length = XmlUtilities.parseInt(startElement.getAttribute("length")); + String kindStr = startElement.getAttribute("kind"); + boolean membersOnly = true; + XmlElement element = parser.peek(); + while (element != null && element.isStart()) { + element = parser.start("member"); + PdbXmlMember pdbXmlMember = pdbParser.getPdbXmlMember(element); + memberList.add(pdbXmlMember); + membersOnly &= (pdbXmlMember.kind == PdbXmlKind.MEMBER); + parser.end(element); + element = parser.peek(); + } + parser.end(startElement); + this.hasNormalMembersOnly = membersOnly; + this.isClass = "class".equals(startElement.getName()) || isInferredClass(kindStr); + this.kind = isClass ? PdbXmlKind.STRUCTURE : PdbXmlKind.parse(kindStr); + } + + private boolean isInferredClass(String kindStr) { + + for (PdbXmlMember m : memberList) { + if (m.kind == PdbXmlKind.MEMBER) { + continue; + } + if ("void *".equals(m.memberDataTypeName)) { + return true; + } + if ("Function".equals(m.memberDataTypeName)) { // ?? + return true; + } + } + return false; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CompositeDefinition other = (CompositeDefinition) obj; + return isClass == other.isClass && kind == other.kind && length == other.length && + SystemUtilities.isEqual(name, other.name); + } + } } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyStackVariables.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyStackVariables.java index 1e7fa0996c..45acfeea0b 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyStackVariables.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyStackVariables.java @@ -16,7 +16,7 @@ package ghidra.app.util.bin.format.pdb; import ghidra.app.cmd.function.CallDepthChangeInfo; -import ghidra.app.util.bin.format.pdb.PdbParserNEW.WrappedDataType; +import ghidra.app.util.bin.format.pdb.PdbParserNEW.PdbXmlMember; import ghidra.app.util.importer.MessageLog; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; @@ -56,25 +56,25 @@ class ApplyStackVariables { } elem = xmlParser.next();//stack variable number start tag - PdbMember member = new PdbMember(elem, monitor); + PdbXmlMember member = pdbParser.getPdbXmlMember(elem); - if ("StaticLocal".equals(member.memberKind)) { + if (PdbXmlKind.STATIC_LOCAL == member.kind) { xmlParser.next();//stack variable number end tag continue; } - DataType dt = getDataType(member, log, monitor); + DataType dt = getDataType(member, log); if (dt == null) { continue; } - if ("ObjectPointer".equals(member.memberKind)) { + if (PdbXmlKind.OBJECT_POINTER == member.kind) { createRegisterParameter(member.memberName, dt, log); } - else if ("Parameter".equals(member.memberKind)) { + else if (PdbXmlKind.PARAMETER == member.kind) { createStackVariable(member.memberName, frameBase + member.memberOffset, dt, log); } - else if ("Local".equals(member.memberKind)) { + else if (PdbXmlKind.LOCAL == member.kind) { createStackVariable(member.memberName, frameBase + member.memberOffset, dt, log); } @@ -188,20 +188,19 @@ class ApplyStackVariables { return variable; } - private DataType getDataType(PdbMember member, MessageLog log, TaskMonitor monitor) - throws CancelledException { - WrappedDataType wrappedDataType = - pdbParser.findDataType(member.memberDataTypeName, monitor); + private DataType getDataType(PdbXmlMember member, MessageLog log) throws CancelledException { + WrappedDataType wrappedDataType = pdbParser.findDataType(member.memberDataTypeName); if (wrappedDataType == null) { - log.appendMsg("Error: failed to resolve data type for " + member.memberKind + ": " + + log.appendMsg("Error: failed to resolve data type for " + member.kind + ": " + member.memberDataTypeName); return null; } - if (wrappedDataType.isZeroLengthArray) { - log.appendMsg("Error: zero length array not supported for for " + member.memberKind + - ": " + member.memberDataTypeName); + if (wrappedDataType.isZeroLengthArray()) { + log.appendMsg("Error: zero length array not supported for for " + member.kind + ": " + + member.memberDataTypeName); return null; } - return wrappedDataType.dataType; + return wrappedDataType.getDataType(); } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyTypeDefs.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyTypeDefs.java index 5e87210203..9b9f2131af 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyTypeDefs.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/ApplyTypeDefs.java @@ -20,7 +20,6 @@ import java.util.List; import org.xml.sax.SAXParseException; -import ghidra.app.util.bin.format.pdb.PdbParserNEW.WrappedDataType; import ghidra.app.util.importer.MessageLog; import ghidra.program.model.data.TypedefDataType; import ghidra.program.model.symbol.SymbolUtilities; @@ -98,19 +97,20 @@ class ApplyTypeDefs { continue;//TODO is this actually a global function } - WrappedDataType baseDataType = pdbParser.findDataType(baseDatatypeName, monitor); + WrappedDataType baseDataType = pdbParser.findDataType(baseDatatypeName); if (baseDataType == null) { log.appendMsg("Error: failed to resolve typedef: " + datatypeName + " -> " + baseDatatypeName); continue; } - if (baseDataType.isZeroLengthArray) { + if (baseDataType.isZeroLengthArray()) { log.appendMsg( "Error: zero length array not supported for typedef: " + datatypeName); continue; } - TypedefDataType typedef = pdbParser.createTypeDef(datatypeName, baseDataType.dataType); + TypedefDataType typedef = + pdbParser.createTypeDef(datatypeName, baseDataType.getDataType()); pdbParser.cacheDataType(datatypeName, typedef); // cache with namespace-based name } } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/BitFieldGroupCompositeMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/BitFieldGroupCompositeMember.java new file mode 100644 index 0000000000..b96d626cef --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/BitFieldGroupCompositeMember.java @@ -0,0 +1,160 @@ +/* ### + * 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.util.bin.format.pdb; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.program.model.data.BitFieldDataType; +import ghidra.program.model.data.DataType; + +/** + * BitFieldGroupCompositeMember provides the ability to collect related + * {@link DefaultCompositeMember} members within a group during the composite reconstruction + * process. + */ +public class BitFieldGroupCompositeMember extends CompositeMember { + + private List list = new ArrayList<>(); + + @Override + boolean isBitFieldMember() { + return true; + } + + @Override + boolean isSingleBitFieldMember() { + return false; + } + + @Override + boolean isContainer() { + return false; + } + + @Override + boolean isStructureContainer() { + return false; + } + + @Override + boolean isUnionContainer() { + return false; + } + + @Override + int getOffset() { + if (list.isEmpty()) { + return 0; + } + return list.get(0).getOffset(); + } + + int getConsumedBits() { + // TODO: this could be maintained as a field + int consumed = 0; + for (DefaultCompositeMember m : list) { + consumed += ((BitFieldDataType) m.getDataType()).getBitSize(); + } + return consumed; + } + + @Override + void setOffset(int offset) { + for (DefaultCompositeMember m : list) { + m.setOffset(offset); + } + } + + @Override + int getLength() { + if (list.isEmpty()) { + return 0; + } + return list.get(0).getLength(); + } + + @Override + DefaultCompositeMember getParent() { + if (list.isEmpty()) { + return null; + } + return list.get(0).getParent(); + } + + @Override + void setParent(DefaultCompositeMember newParent) { + for (DefaultCompositeMember m : list) { + m.setParent(newParent); + } + } + + @Override + boolean addMember(DefaultCompositeMember member) { + + DataType dt = member.getDataType(); + if (dt == null || dt.getLength() <= 0) { + return false; + } + + // trigger structure/union transformation + DefaultCompositeMember bf0 = list.remove(0); + + return bf0.addMember(member); + } + + @Override + boolean addToStructure(DefaultCompositeMember structure) { + // add all bit-fields to structure and allow them to regroup + boolean success = true; + for (DefaultCompositeMember m : list) { + m.setBitFieldGroup(null); + success &= m.addToStructure(structure); + } + return success; + } + + @Override + void finalizeDataType(int preferredSize) { + return; // nothing to do + } + + private DefaultCompositeMember validateNewMember(CompositeMember member) { + if (!member.isSingleBitFieldMember()) { + throw new IllegalArgumentException("expected single bit-field member"); + } + if (!list.isEmpty() && + (member.getOffset() != getOffset() || member.getLength() != getLength())) { + throw new IllegalArgumentException( + "expected bit-field member with same offset and length"); + } + DefaultCompositeMember m = (DefaultCompositeMember) member; + m.setBitFieldGroup(this); + return m; + } + + /** + * Add a new member to the end of this bit-field group. The caller should ensure that the + * specified member is a suitable addition to this group (must be single bit field whose + * member offset and length match this group's). + * @param member bit-field member (must have data type of BitFieldDataType). + * @throws IllegalArgumentException if specified member is not suitable for this group. + */ + void addToGroup(CompositeMember member) { + list.add(validateNewMember(member)); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/CompositeMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/CompositeMember.java index 176a264f2e..310d30295b 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/CompositeMember.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/CompositeMember.java @@ -15,180 +15,13 @@ */ package ghidra.app.util.bin.format.pdb; -import java.util.*; - -import ghidra.app.util.bin.format.pdb.PdbParserNEW.WrappedDataType; -import ghidra.program.model.data.*; -import ghidra.util.*; -import ghidra.util.datastruct.RangeMap; -import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; -import ghidra.util.xml.XmlUtilities; -import ghidra.xml.XmlTreeNode; +import ghidra.program.model.data.BitFieldDataType; /** - * CompositeMember provides the ability to process PDB data-type records and - * incrementally build-up composite structure and union data-types from a flattened offset-based - * list of members which may include embedded anonymous composite members. Composite members - * correspond to either hard predefined data-types, or structure/union containers whose members - * are added and refined incrementally. - *

- * Container members are characterized by a null data-type name, zero length, and will be - * identified as either a structure or union. + * CompositeMember provides a composite construction member interface for use + * by the PDB parser. */ -class CompositeMember { - - private static int MAX_CONSTRUCTION_DEPTH = 20; - - private DataTypeManager dataTypeManager; - - private CompositeMember parent; // parent container (null if this is root container) - - private String memberName; // null if this is a root container - private String memberDataTypeName; // null if this is a container - private int memberOffset; // member offset relative to start of parent container - private String memberKind; // PDB defined kind of data type (e.g., Structure, Union) - private int memberLength; // container members have 0 length (rely on memberDataType) - - private DataType memberDataType; - private boolean memberIsZeroLengthArray; - - private DataTypeResolver memberDataTypeResolver; - - // Structure container data - private Map structureMemberOffsetMap; - private RangeMap structureMemberRangeMap; - - // Union container data - private List unionMemberList; - private boolean isBitFieldUnion; - private int bitFieldUnionLength; - - private static long nextTemporaryValue; - - private static synchronized String allocateTemporaryContainerName() { - return "_tmp_" + nextTemporaryValue++; - } - - /** - * Construct the outermost root container member for a new composite data-type. - * @param dataTypeResolver data-type resolver - * @param monitor task monitor - * @throws CancelledException if task is cancelled - */ - CompositeMember(DataTypeResolver dataTypeResolver, DataTypeManager dataTypeManager) - throws CancelledException { - memberOffset = -1; - memberDataTypeResolver = dataTypeResolver; - this.dataTypeManager = dataTypeManager; - resolve(); - } - - /** - * Construct a new composite member from a PDB data-type member record. - * @param member PDB member record - * @param dataTypeResolver data-type resolver - * @param monitor task monitor - * @throws CancelledException if task is cancelled - */ - private CompositeMember(PdbMember member, DataTypeResolver dataTypeResolver, - DataTypeManager dataTypeManager, TaskMonitor monitor) throws CancelledException { - memberName = member.memberName; - memberDataTypeName = member.memberDataTypeName; - memberOffset = member.memberOffset; - memberKind = member.memberKind; - memberLength = member.memberLength; - memberDataTypeResolver = dataTypeResolver; - this.dataTypeManager = dataTypeManager; - resolve(); - } - - /** - * Construct a new composite member by cloning an existing member. - * @param member composite member to be cloned - */ - private CompositeMember(CompositeMember member) { - memberName = member.memberName; - memberDataTypeName = member.memberDataTypeName; - memberDataType = member.memberDataType; - memberIsZeroLengthArray = member.memberIsZeroLengthArray; - memberOffset = member.memberOffset; - memberKind = member.memberKind; - memberLength = member.memberLength; - memberDataTypeResolver = member.memberDataTypeResolver; - dataTypeManager = member.dataTypeManager; - structureMemberOffsetMap = member.structureMemberOffsetMap; - structureMemberRangeMap = member.structureMemberRangeMap; - unionMemberList = member.unionMemberList; - isBitFieldUnion = member.isBitFieldUnion; - bitFieldUnionLength = member.bitFieldUnionLength; - } - - /** - * Get member name to be used within parent composite definition - * @return member name or null if this is root container - */ - String getName() { - return memberName; - } - - /** - * Get the PDB defined KIND designator for this member (e.g., Structure, Union) - * @return PDB defined KIND of member - */ - String getKind() { - return memberKind; - } - - /** - * Get the data type name associated with this member. Anonymous inner composite - * types will utilize a generated named based upon its parent type name and the - * offset at which it occurs within its parent. - * @return data type name associated with this member - */ - String getDataTypeName() { - return memberDataType != null ? memberDataType.getName() : memberDataTypeName; - } - - /** - * Get the data type associated with this member. Container members data-type - * may continue to transform as additional members are added. - * @return data type associated with this member. - */ - DataType getDataType() { - return memberDataType; - } - - private void updateContainerNameAndCategoryPath(String typeMnemonic) { - if (parent == null || !isContainer()) { - return; // only non-root container may be renamed - } - String baseName = parent.getDataTypeName(); - String oldMemberName = memberName; - String name = "_" + typeMnemonic + "_"; - if (parent.isUnionContainer()) { - try { - name += parent.getOrdinal(oldMemberName); - } - catch (NotFoundException e) { - Msg.error(this, "Failed to rename anonymous compsite: " + getDataTypeName()); - } - } - else { - name += memberOffset; - } - try { - memberDataType.setName(baseName + name); - memberDataType.setCategoryPath(parent.getChildCategoryPath()); - - memberName = name; - parent.memberNameChanged(oldMemberName, memberName); - } - catch (InvalidNameException | DuplicateNameException e) { - // exceptions are unexpected - throw new AssertException(e); - } - } +abstract class CompositeMember { /** * Due to the dynamic restructuring of data type containers, this method should be invoked @@ -197,624 +30,82 @@ class CompositeMember { * unions to reflect the final organization and check if internal alignment should be enabled. * @param preferredSize preferred size of composite if known, else <= 0 if unknown */ - void finalizeDataType(int preferredSize) { - if (!isContainer()) { - return; - } - if (isStructureContainer()) { - updateContainerNameAndCategoryPath("s"); - CompositeMember lastMember = null; - for (CompositeMember member : structureMemberOffsetMap.values()) { - member.finalizeDataType(0); - lastMember = member; - } - if (lastMember != null && lastMember.memberIsZeroLengthArray) { - // transform last member into flexible array - Structure struct = (Structure) memberDataType; - Array array = (Array) lastMember.getDataType(); - struct.setFlexibleArrayComponent(array.getDataType(), lastMember.getName(), null); - struct.delete(struct.getNumComponents() - 1); - } - } - else if (isUnionContainer()) { - if (isBitFieldUnionContainer()) { - updateContainerNameAndCategoryPath("bitfield"); - } - else { - updateContainerNameAndCategoryPath("u"); - for (CompositeMember member : unionMemberList) { - member.finalizeDataType(0); - } - } - } - if (testContainerAlignment(preferredSize)) { - ((Composite) memberDataType).setInternallyAligned(true); - } - } + abstract void finalizeDataType(int preferredSize); /** - * Determine is a container type should enable alignment. - * @param preferredSize preferred size of composite if known, else <= 0 if unknown - * @return true if internal structure alignment should be enabled, else false + * Determine if this member is a container + * @return true if container, else false */ - private boolean testContainerAlignment(int preferredSize) { - Composite copy = (Composite) memberDataType.copy(dataTypeManager); - copy.setInternallyAligned(true); - if (preferredSize <= 0) { - // assume anonymous composites are aligned if size does not change - return copy.getLength() == memberDataType.getLength(); - } - // use alignment if length matches preferredSize - return copy.getLength() == preferredSize; - } + abstract boolean isContainer(); + + /** + * Determine if this member is a union container + * @return true if union container, else false + */ + abstract boolean isUnionContainer(); + + /** + * Determine if this member is a structure container + * @return true if structure container, else false + */ + abstract boolean isStructureContainer(); + + /** + * Determine if this member is a bit-field member or group. + * @return true if bit-field member, else false + */ + abstract boolean isBitFieldMember(); + + /** + * Determine if this member is a bit-field member not yet contained within a group. + * If true is returned this instance is ensured to be a {@link DefaultCompositeMember} instance + * whose data type is {@link BitFieldDataType}. + * @return true if bit-field member not yet contained within a group + */ + abstract boolean isSingleBitFieldMember(); /** * Get the offset of this member relative to the start of its parent container. * @return relative member offset or -1 for root container */ - int getOffset() { - return memberOffset; - } + abstract int getOffset(); + + /** + * Set the offset of this member relative to the start of its parent container. + * @param offset relative member offset + */ + abstract void setOffset(int offset); /** * Get the data type length associated with this member. Container members data-type * length may continue to grow as additional members are added. * @return data type associated with this member. */ - int getLength() { - return memberDataType != null ? memberDataType.getLength() : memberLength; - } - - private void resolve() throws CancelledException { - WrappedDataType wrappedDataType = memberDataTypeResolver.resolveDataType(this); - if (wrappedDataType != null) { - memberDataType = wrappedDataType.dataType.clone(dataTypeManager); - memberIsZeroLengthArray = wrappedDataType.isZeroLengthArray; - } - if (isContainer()) { - initializeContainer(); - } - } - - private void initializeContainer() { - if (!(memberDataType instanceof Composite)) { - throw new AssertException("Root must resolve to a composite type"); - } - if (memberDataType instanceof Structure) { - memberKind = PdbParserNEW.STRUCTURE_KIND; - structureMemberOffsetMap = new TreeMap<>(); - structureMemberRangeMap = new RangeMap(-1); - unionMemberList = null; - } - else { - memberKind = PdbParserNEW.UNION_KIND; - unionMemberList = new ArrayList<>(); - structureMemberOffsetMap = null; - structureMemberRangeMap = null; - } - isBitFieldUnion = false; - bitFieldUnionLength = 0; - memberLength = 0; // compositeMemberLength is preserved - } + abstract int getLength(); /** - * Determine if this member is a container - * @return true if container, else false + * Get the parent which corresponds to this member + * @return parent */ - boolean isContainer() { - return memberDataTypeName == null; - } + abstract DefaultCompositeMember getParent(); /** - * Determine if this member is a union container - * @return true if union container, else false + * Set the composite parent which contains this member + * @param parent new parent */ - boolean isUnionContainer() { - return unionMemberList != null; - } + abstract void setParent(DefaultCompositeMember parent); /** - * Determine if this member is a structure container - * @return true if structure container, else false + * Add specified member to this member. If this member is not a composite + * it will trigger the creation + * @param member + * @return */ - boolean isStructureContainer() { - return structureMemberOffsetMap != null; - } + abstract boolean addMember(DefaultCompositeMember member); /** - * Determine if this member is a bit-field member - * @return true if bit-field member, else false + * Instructs this member to add itself to the specified structure + * @param structure composite structure */ - boolean isBitFieldMember() { - if (memberName == null || memberLength == 0) { - return false; - } - int colonPos = memberName.indexOf(':'); - if (colonPos == -1) { - return false; - } - int nextColonPos = memberName.indexOf(':', colonPos + 1); - if (nextColonPos != -1) { - return false; - } - String[] split = memberName.split(":"); - try { - int bitIndex = XmlUtilities.parseInt(split[1]); - if (bitIndex < 0) { - return false; - } - } - catch (Exception e) { - return false; - } - return true; - } - - private boolean isBitFieldUnionContainer() { - if (!isUnionContainer() || unionMemberList.size() < 2) { - return false; - } - CompositeMember member0 = unionMemberList.get(0); - CompositeMember member1 = unionMemberList.get(1); - return member0.isBitFieldMember() && member0.isCompanionBitField(member1); - } - - private int getDepth() { - int depth = 0; - CompositeMember p = parent; - while (p != null) { - p = p.parent; - ++depth; - } - return depth; - } - - @Override - public String toString() { - String type; - if (isUnionContainer()) { - type = PdbParserNEW.UNION_KIND; - } - else if (isStructureContainer()) { - type = PdbParserNEW.STRUCTURE_KIND; - } - else { - type = memberDataTypeName; - } - return "[CompositeMember: " + memberOffset + " " + memberName + " " + type + "]"; - } - - /** - * DataTypeResolver provides the ability to resolve a member's data-type - * at the time of construction. - */ - interface DataTypeResolver { - /** - * Find the specified member's data type based upon its' data-type name - * @param member composite member to be resolved - * @return data-type which corresponds to the specified member's data-type name or null - * if unable to resolve. - * @throws CancelledException if operation cancelled - */ - WrappedDataType resolveDataType(CompositeMember member) throws CancelledException; - } - - /** - * Attempt to add a child member to this composite hierarchy - * @param child PDB data-type member record - * @param monitor task monitor - * @return true if child data type resolved and it was successfully added to composite hierarchy, - * false if unable to resolve member's data-type or other error occurred. - * NOTE: there may be complex hierarchies not yet handled. - * @throws CancelledException if operation cancelled - */ - boolean addMember(PdbMember child, TaskMonitor monitor) throws CancelledException { - - if (!isContainer()) { - throw new AssertException("addMember only permitted on root members"); - } - if (!(memberDataType instanceof Composite)) { - throw new AssertException(); - } - - if (!child.memberKind.equals("Member")) { - throw new AssertException(); - } - - return addMember( - new CompositeMember(child, memberDataTypeResolver, dataTypeManager, monitor)); - } - - private CategoryPath getChildCategoryPath() { - return new CategoryPath(memberDataType.getCategoryPath(), getDataTypeName()); - } - - private String getOutermostDataTypeName() { - if (parent != null) { - return parent.getOutermostDataTypeName(); - } - return getDataTypeName(); - } - - private boolean transformIntoUnionContainer() { - - if (parent == null) { - throw new AssertException(); - } - - if (getDepth() >= MAX_CONSTRUCTION_DEPTH) { - Msg.error(this, "PDB composite reconstruction exceeded maximum allowed depth: " + - getOutermostDataTypeName()); - return false; - } - - // Remove siblings from parent whose offsets are greater - List elderSiblings = kidnapElderSiblingsFromParentStructure(); - - CompositeMember memberCopy = new CompositeMember(this); - memberCopy.memberOffset = 0; - - CategoryPath tempCategoryPath = parent.getDataType().getCategoryPath(); - String tempName = allocateTemporaryContainerName(); - - Union nestedUnion = new UnionDataType(tempCategoryPath, tempName, dataTypeManager); - - nestedUnion.add(memberDataType, memberName, null); - - String oldName = memberName; - memberName = tempName; - memberDataType = nestedUnion; - memberIsZeroLengthArray = false; - memberDataTypeName = null; // signifies a container - initializeContainer(); - - unionMemberList.add(memberCopy); - memberCopy.parent = this; - - if (!elderSiblings.isEmpty()) { - memberCopy.transformIntoStructureContainer(); - for (CompositeMember sibling : elderSiblings) { - sibling.memberOffset -= memberOffset; - if (!memberCopy.addStructureMember(sibling)) { - return false; - } - } - } - - isBitFieldUnion = memberCopy.isBitFieldMember(); - if (isBitFieldUnion) { - bitFieldUnionLength = memberCopy.memberLength; // bit field length is bit-length - } - - if (parent != null) { - parent.memberChanged(oldName, this); - } - - return true; - } - - private boolean transformIntoStructureContainer() { - - if (parent == null) { - throw new AssertException(); - } - - if (getDepth() >= MAX_CONSTRUCTION_DEPTH) { - Msg.error(this, "PDB composite reconstruction exceeded maximum allowed depth: " + - getOutermostDataTypeName()); - return false; - } - - CompositeMember memberCopy = new CompositeMember(this); - memberCopy.memberOffset = 0; - - CategoryPath tempCategoryPath = parent.getDataType().getCategoryPath(); - String tempName = allocateTemporaryContainerName(); - - Structure nestedStructure = - new StructureDataType(tempCategoryPath, tempName, 0, dataTypeManager); - - nestedStructure.insertAtOffset(0, memberDataType, memberDataType.getLength(), memberName, - getStructureMemberComment()); - - String oldName = memberName; - memberName = tempName; - memberDataType = nestedStructure; - memberIsZeroLengthArray = false; - memberDataTypeName = null; // signifies a container - initializeContainer(); - - structureMemberRangeMap.paintRange(0, memberCopy.getLength() - 1, 0); - structureMemberOffsetMap.put(0, memberCopy); - memberCopy.parent = this; - - if (parent != null) { - parent.memberChanged(oldName, this); - } - return true; - } - - private String getStructureMemberComment() { - if (memberIsZeroLengthArray) { - return "warning: zero length array forced to have one element"; - } - return null; - } - - private boolean addStructureMember(CompositeMember member) { - - // check for conflict within structure container - int conflictOffset = structureMemberRangeMap.getValue(member.memberOffset); - if (conflictOffset < 0) { - structureMemberOffsetMap.put(member.memberOffset, member); - structureMemberRangeMap.paintRange(member.memberOffset, - member.memberOffset + member.getLength() - 1, member.memberOffset); - member.parent = this; - ((Structure) memberDataType).insertAtOffset(member.memberOffset, member.memberDataType, - member.getLength(), member.memberName, member.getStructureMemberComment()); - if (parent != null) { - parent.sizeChanged(this); - } - return true; - } - - CompositeMember conflictMember = structureMemberOffsetMap.get(conflictOffset); - - // adjust this member for addition to container - member.memberOffset -= conflictMember.memberOffset; - - return conflictMember.addMember(member); - } - - private boolean addUnionMember(CompositeMember member) { - - if (member.memberOffset == 0) { - if (isBitFieldUnion && !isCompanionBitField(member)) { - // push this union into a new union - transformIntoUnionContainer(); - return addUnionMember(member); // try again - } - - if (!isBitFieldUnion && unionMemberList.size() != 0 && member.isBitFieldMember()) { - CompositeMember lastUnionMember = unionMemberList.get(unionMemberList.size() - 1); - if (lastUnionMember.isCompanionBitField(member)) { - return lastUnionMember.addMember(member); - } - } - - unionMemberList.add(member); - member.parent = this; - ((Union) memberDataType).add(member.memberDataType, member.memberName, null); - if (isBitFieldUnion) { - bitFieldUnionLength += member.memberLength; - } - if (parent != null) { - parent.sizeChanged(this); - } - return true; - } - - // find relevant union member for structure conversion - for (CompositeMember unionMember : unionMemberList) { - if ((member.isBitFieldMember() && unionMember.isCompanionBitField(member)) || - (!member.isBitFieldMember() && - member.memberOffset >= (unionMember.memberOffset + unionMember.getLength()))) { - // NOTE: Placement is rather speculative - assume structure is required - // TODO: watch out for nested union - member.memberOffset -= unionMember.memberOffset; - return unionMember.addMember(member); - } - } - - CompositeMember lastUnionMember = unionMemberList.get(unionMemberList.size() - 1); - // NOTE: union must be forced into structure transformation - if (lastUnionMember.isUnionContainer()) { - if (!lastUnionMember.transformIntoStructureContainer()) { - return false; - } - } - return lastUnionMember.addMember(member); - } - - private boolean isCompanionBitField(CompositeMember member) { - if (!member.isBitFieldMember()) { - return false; - } - if (isContainer()) { - if (isBitFieldUnion) { - if (unionMemberList.size() == 0 || member.memberOffset != 0) { - return false; - } - if (!SystemUtilities.isEqual(unionMemberList.get(0).memberDataTypeName, - member.memberDataTypeName)) { - return false; - } - int combinedBitfieldLength = bitFieldUnionLength + member.memberLength; - if (combinedBitfieldLength > (member.getDataType().getLength() * 8)) { - return false; - } - return true; - } - //return member.memberOffset < getLength(); - return false; - } - if (!isBitFieldMember()) { - return false; - } - if (memberOffset != member.memberOffset) { - return false; - } - return SystemUtilities.isEqual(memberDataTypeName, member.memberDataTypeName); - } - - private void sizeChanged(CompositeMember pdbMember) { - if (structureMemberRangeMap != null) { - structureMemberRangeMap.paintRange(pdbMember.memberOffset, - pdbMember.memberOffset + pdbMember.getLength() - 1, pdbMember.memberOffset); - } - if (parent != null) { - parent.sizeChanged(this); - } - } - - private void memberChanged(String fieldName, CompositeMember newMember) { - if (isUnionContainer()) { - Union union = (Union) memberDataType; - int count = union.getNumComponents(); - for (int i = 0; i < count; i++) { - DataTypeComponent component = union.getComponent(i); - if (fieldName.equals(component.getFieldName())) { - union.delete(i); - union.insert(i, newMember.getDataType(), newMember.getLength(), - newMember.memberName, null); - break; - } - } - } - else if (isStructureContainer()) { - Structure struct = (Structure) memberDataType; - struct.replaceAtOffset(newMember.getOffset(), newMember.getDataType(), - newMember.getLength(), newMember.getName(), null); - } - } - - private void memberNameChanged(String oldFieldName, String newFieldName) { - if (isContainer()) { - Composite composite = (Composite) memberDataType; - int count = composite.getNumComponents(); - for (int i = 0; i < count; i++) { - DataTypeComponent component = composite.getComponent(i); - if (oldFieldName.equals(component.getFieldName())) { - try { - component.setFieldName(newFieldName); - } - catch (DuplicateNameException e) { - Msg.error(this, "Failed to rename temporary component name: " + - getDataTypeName() + "." + oldFieldName + " -> " + newFieldName); - } - break; - } - } - } - } - - private int getOrdinal(String fieldName) throws NotFoundException { - if (!isContainer()) { - throw new AssertException(); - } - Composite composite = (Composite) memberDataType; - int count = composite.getNumComponents(); - for (int i = 0; i < count; i++) { - DataTypeComponent component = composite.getComponent(i); - if (fieldName.equals(component.getFieldName())) { - return i; - } - } - throw new NotFoundException(); - } - - private boolean addMember(CompositeMember member) { - - if (member.memberDataType == null || member.memberDataType.getLength() <= 0) { - return false; - } - - if (!isContainer()) { - if (member.memberOffset != 0) { - if (!transformIntoStructureContainer()) { - return false; - } - } - else { - if (!transformIntoUnionContainer()) { - return false; - } - } - } - - if (isUnionContainer()) { - return addUnionMember(member); - } - return addStructureMember(member); - } - - /** - * This method facilitates the removal and collection of all siblings of this - * member from its parent container. Only those siblings whose offset is greater - * than this member's offset will be included. The use of this method is necessary when - * a member sequence has been added to a structure container and it is later decided to - * push this member and its siblings into a new sub-composite. Before they can be - * added to the new container they must be removed from their current container - * using this method. - * @return list of sibling structure members removed from parent - */ - private List kidnapElderSiblingsFromParentStructure() { - - List list = new ArrayList<>(); - if (parent == null || !parent.isStructureContainer()) { - return list; - } - - Structure parentStruct = (Structure) parent.memberDataType; - - for (DataTypeComponent component : parentStruct.getComponents()) { - if (component.getOffset() > memberOffset) { - parentStruct.clearComponent(component.getOrdinal()); - CompositeMember member = - parent.structureMemberOffsetMap.remove(component.getOffset()); - // could be a padding undefined that was never added to the structureMemberOffsetMap - if (member != null) { - list.add(member); - } - else if (component.getDataType() != DataType.DEFAULT) { - // exceptions are unexpected - throw new AssertException("Data Type component parsing issues " + - parentStruct.getName() + " kidnapping " + component.getFieldName()); - } - } - } - parent.structureMemberRangeMap.paintRange(memberOffset + getLength(), parent.getLength(), - -1); - return list; - } - - /** - * Buildup an empty composite by applying datatype composite members defined as - * children of an PDB XML class or datatype node. Only those children with a kind of - * "Member" will be processed. - * @param pdbParser PDB parser object - * @param composite empty composite to which members will be added - * @param preferredCompositeSize preferred size of composite, <= 0 indicates unknown - * @param compositeNode PDB XML class or datatype node whose children will be processed - * @param monitor task monitor - * @return true if members successfully added to composite - * @throws CancelledException if monitor is cancelled - */ - static boolean applyDataTypeMembers(PdbParserNEW pdbParser, Composite composite, - int preferredCompositeSize, XmlTreeNode compositeNode, TaskMonitor monitor) - throws CancelledException { - - Composite editComposite = composite; - - CompositeMember rootMember = new CompositeMember( - member -> member.isContainer() ? new WrappedDataType(editComposite, false) - : pdbParser.findDataType(member.getDataTypeName(), monitor), - pdbParser.getProgramDataTypeManager()); - - Iterator children = compositeNode.getChildren(); - while (children.hasNext()) { - monitor.checkCanceled(); - XmlTreeNode child = children.next(); - PdbMember member = new PdbMember(child, monitor); - if (member.memberKind.equals("Member")) { - if (!rootMember.addMember(member, monitor)) { - return false; - } - } - } - - rootMember.finalizeDataType(preferredCompositeSize); - return true; - } - + abstract boolean addToStructure(DefaultCompositeMember structure); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java new file mode 100644 index 0000000000..58fd377206 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java @@ -0,0 +1,1149 @@ +/* ### + * 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.util.bin.format.pdb; + +import java.util.*; +import java.util.function.Consumer; + +import ghidra.program.model.data.*; +import ghidra.util.InvalidNameException; +import ghidra.util.Msg; +import ghidra.util.datastruct.RangeMap; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +/** + * CompositeMember provides the ability to process PDB data-type records and + * incrementally build-up composite structure and union data-types from a flattened offset-based + * list of members which may include embedded anonymous composite members. Composite members + * correspond to either hard predefined data-types, or structure/union containers whose members + * are added and refined incrementally. + *

+ * Container members are characterized by a null data-type name, zero length, and will be + * identified as either a structure or union. + */ +class DefaultCompositeMember extends CompositeMember { + + private static int MAX_CONSTRUCTION_DEPTH = 20; + + private DataTypeManager dataTypeManager; + private Consumer errorConsumer; + + private DefaultCompositeMember parent; // parent container (null if this is root container) + + private boolean isClass; // true for root container which corresponds to class structure + + private String memberName; // null if this is a root container + private String memberDataTypeName; // null if this is a container + private int memberOffset; // member offset relative to start of parent container + private MemberType memberType; // type of member (e.g., STRUCTURE, UNION, MEMBER) + private int memberLength; // container members have 0 length (rely on memberDataType) + + private DataType memberDataType; + private boolean memberIsZeroLengthArray; + private BitFieldGroupCompositeMember bitFieldGroup; + + // Structure container data + private Map structureMemberOffsetMap; + private RangeMap structureMemberRangeMap; + + // Union container data + private List unionMemberList; + + private static long nextTemporaryValue; + + private static synchronized String allocateTemporaryContainerName(String type) { + return "_tmp_" + type + nextTemporaryValue++; + } + + /** + * Construct the outermost root container member for a new composite data-type. + * @param isClass true if container corresponds to a Class structure, else false + * @param editComposite composite to be built-up (must have program's datatype manager) + * @param errorConsumer error consumer (may be null) + * @throws CancelledException if task is cancelled + */ + private DefaultCompositeMember(boolean isClass, Composite editComposite, + Consumer errorConsumer) throws CancelledException { + this.isClass = isClass; + memberDataType = editComposite; + memberOffset = -1; + this.dataTypeManager = editComposite.getDataTypeManager(); + this.errorConsumer = errorConsumer; + initializeContainer(); + } + + /** + * Construct a new composite member from a PDB data-type member record. + * @param member PDB member record + * @param dataTypeManager program's datatype manager + * @param errorConsumer error consumer (may be null) + * @param monitor task monitor + * @throws CancelledException if task is cancelled + * @throws DataTypeDependencyException if datatype dependency cannot be resolved + */ + private DefaultCompositeMember(PdbMember member, DataTypeManager dataTypeManager, + Consumer errorConsumer, TaskMonitor monitor) + throws DataTypeDependencyException, CancelledException { + + memberName = member.memberName; + memberDataTypeName = member.memberDataTypeName; + memberOffset = member.memberOffset; + memberType = MemberType.MEMBER; + memberLength = 0; // n/a for regular members + this.dataTypeManager = dataTypeManager; + + WrappedDataType wrappedDataType = member.getDataType(); + if (wrappedDataType == null) { + throw new DataTypeDependencyException( + "Failed to resolve datatype " + memberDataTypeName + " " + memberName); + } + memberDataType = wrappedDataType.getDataType().clone(dataTypeManager); + memberIsZeroLengthArray = wrappedDataType.isZeroLengthArray(); + } + + /** + * Construct a new composite member by cloning an existing member. + * This is intended for use when establishing nested anonymous unions and structures. + * @param member composite member to be cloned + */ + private DefaultCompositeMember(DefaultCompositeMember member) { + memberName = member.memberName; + memberDataTypeName = member.memberDataTypeName; + memberDataType = member.memberDataType; + memberIsZeroLengthArray = member.memberIsZeroLengthArray; + memberOffset = member.memberOffset; + memberType = member.memberType; + memberLength = member.memberLength; + errorConsumer = member.errorConsumer; + dataTypeManager = member.dataTypeManager; + structureMemberOffsetMap = member.structureMemberOffsetMap; + structureMemberRangeMap = member.structureMemberRangeMap; + unionMemberList = member.unionMemberList; + } + + /** + * Construct a filler/padding bitfield member + * @param componentOffset member offset within parent + * @param baseDataType bitfield base datatype + * @param bitSize bitfield size in bits + * @param bitOffsetWithinBaseType offset of bitfield within base type + * @throws InvalidDataTypeException + */ + private DefaultCompositeMember(int componentOffset, DataType baseDataType, int bitSize, + int bitOffsetWithinBaseType) throws InvalidDataTypeException { + memberName = "padding"; + memberDataType = new PdbBitField(baseDataType, bitSize, bitOffsetWithinBaseType); + memberIsZeroLengthArray = false; + memberOffset = componentOffset; + memberType = MemberType.MEMBER; + memberLength = baseDataType.getLength(); + dataTypeManager = baseDataType.getDataTypeManager(); + } + + @Override + DefaultCompositeMember getParent() { + return parent; + } + + @Override + void setParent(DefaultCompositeMember newParent) { + parent = newParent; + } + + /** + * Get member name to be used within parent composite definition + * @return member name or null if this is root container + */ + private String getName() { + return memberName; + } + + /** + * Get the data type name associated with this member. Anonymous inner composite + * types will utilize a generated named based upon its parent type name and the + * offset at which it occurs within its parent. + * @return data type name associated with this member + */ + private String getDataTypeName() { + return memberDataType != null ? memberDataType.getName() : memberDataTypeName; + } + + /** + * Get the data type associated with this member. Container members data-type + * may continue to transform as additional members are added. + * @return data type associated with this member. + */ + DataType getDataType() { + return memberDataType; + } + + private void updateContainerNameAndCategoryPath(String typeMnemonic) { + if (parent == null || !isContainer()) { + return; // only non-root container may be renamed + } + String baseName = parent.getDataTypeName(); + String oldMemberName = memberName; + String name = "_" + typeMnemonic + "_"; + if (parent.isUnionContainer()) { + try { + name += parent.getOrdinal(oldMemberName); + } + catch (NotFoundException e) { + Msg.error(this, "Failed to rename anonymous compsite: " + getDataTypeName()); + } + } + else { + name += memberOffset; + } + try { + memberDataType.setName(baseName + name); + memberDataType.setCategoryPath(parent.getChildCategoryPath()); + + memberName = name; + parent.memberNameChanged(oldMemberName, memberName); + } + catch (InvalidNameException | DuplicateNameException e) { + // exceptions are unexpected + throw new AssertException(e); + } + } + + private void transformLastMemberIntoFlexArray(CompositeMember lastMember) { + if (!(lastMember instanceof DefaultCompositeMember)) { + return; + } + DefaultCompositeMember m = (DefaultCompositeMember) lastMember; + if (m.memberIsZeroLengthArray) { + // transform last member into flexible array + Structure struct = (Structure) memberDataType; + Array array = (Array) m.getDataType(); + struct.setFlexibleArrayComponent(array.getDataType(), m.getName(), null); + struct.delete(struct.getNumComponents() - 1); + } + } + + @Override + void finalizeDataType(int preferredSize) { + if (!isContainer()) { + return; + } + if (isStructureContainer()) { + updateContainerNameAndCategoryPath("s"); + CompositeMember lastMember = null; + for (CompositeMember member : structureMemberOffsetMap.values()) { + member.finalizeDataType(0); + lastMember = member; + } + transformLastMemberIntoFlexArray(lastMember); + + // remove trailing fat caused by use of insert operations + trimReconstructionFat(preferredSize); + } + else if (isUnionContainer()) { + updateContainerNameAndCategoryPath("u"); + for (CompositeMember member : unionMemberList) { + member.finalizeDataType(0); + } + } + alignComposite(preferredSize); + } + + private void trimReconstructionFat(int preferredSize) { + if (!isStructureContainer()) { + return; + } + Structure struct = (Structure) getDataType(); + + DataTypeComponent dtc = struct.getComponentAt(preferredSize); + if (dtc == null) { + return; + } + + int startOrdinal = dtc.getOrdinal(); + if (dtc.getOffset() != preferredSize) { + ++startOrdinal; + } + + for (int i = struct.getNumComponents() - 1; i >= startOrdinal; i--) { + DataTypeComponent comp = struct.getComponent(i); + if (comp.getDataType() != DataType.DEFAULT) { + break; + } + struct.delete(i); + } + } + + /** + * Align container composite data type if possible. + * @param preferredSize preferred size of composite if known, else <= 0 if unknown + */ + private void alignComposite(int preferredSize) { + + Composite copy = (Composite) memberDataType.copy(dataTypeManager); + copy.setInternallyAligned(true); + + boolean alignOK = true; + if (preferredSize > 0 && copy.getNumComponents() != 0) { + alignOK = (copy.getLength() == preferredSize); + } + + if (alignOK && isStructureContainer()) { + // verify that components did not move + Structure struct = (Structure) memberDataType; + DataTypeComponent[] unalignedComponents = struct.getDefinedComponents(); + int index = 0; + for (DataTypeComponent dtc : copy.getComponents()) { + DataTypeComponent unalignedDtc = unalignedComponents[index++]; + if (!isComponentUnchanged(dtc, unalignedDtc)) { + alignOK = false; + break; + } + } + } + + if (alignOK) { + ((Composite) memberDataType).setInternallyAligned(true); + } + else if (errorConsumer != null && !isClass) { // don't complain about Class structs which always fail + String anonymousStr = parent != null ? " anonymous " : ""; + errorConsumer.accept("PDB " + anonymousStr + memberType + + " reconstruction failed to align " + memberDataType.getPathName()); + } + } + + private boolean isComponentUnchanged(DataTypeComponent dtc, DataTypeComponent unalignedDtc) { + if (unalignedDtc.getOffset() != dtc.getOffset() || + unalignedDtc.getLength() != dtc.getLength() || + unalignedDtc.isBitFieldComponent() != dtc.isBitFieldComponent()) { + return false; + } + if (dtc.isBitFieldComponent()) { + // both components are bit fields + BitFieldDataType bitfieldDt = (BitFieldDataType) dtc.getDataType(); + BitFieldDataType unalignedBitfieldDt = (BitFieldDataType) unalignedDtc.getDataType(); + if (bitfieldDt.getBitOffset() != unalignedBitfieldDt.getBitOffset() || + bitfieldDt.getBitSize() != unalignedBitfieldDt.getBitSize()) { + return false; + } + } + return true; + } + + @Override + int getOffset() { + return memberOffset; + } + + @Override + void setOffset(int offset) { + memberOffset = offset; + } + + @Override + int getLength() { + if (memberDataType instanceof BitFieldDataType) { + BitFieldDataType bitfield = (BitFieldDataType) memberDataType; + return bitfield.getBaseTypeSize(); + } + return memberDataType != null ? memberDataType.getLength() : memberLength; + } + + private void initializeContainer() { + if (!(memberDataType instanceof Composite)) { + throw new AssertException("Root must resolve to a composite type"); + } + if (memberDataType instanceof Structure) { + memberType = MemberType.STRUCTURE; + structureMemberOffsetMap = new TreeMap<>(); + structureMemberRangeMap = new RangeMap(-1); + unionMemberList = null; + } + else { + if (isClass) { + throw new AssertException(); + } + memberType = MemberType.UNION; + unionMemberList = new ArrayList<>(); + structureMemberOffsetMap = null; + structureMemberRangeMap = null; + } + memberLength = 0; // compositeMemberLength is preserved + } + + /** + * Determine if this member is a container + * @return true if container, else false + */ + @Override + boolean isContainer() { + return memberDataTypeName == null; + } + + /** + * Determine if this member is a union container + * @return true if union container, else false + */ + @Override + boolean isUnionContainer() { + return unionMemberList != null; + } + + /** + * Determine if this member is a structure container + * @return true if structure container, else false + */ + @Override + boolean isStructureContainer() { + return structureMemberOffsetMap != null; + } + + @Override + boolean isBitFieldMember() { + return memberDataType instanceof PdbBitField; + } + + @Override + boolean isSingleBitFieldMember() { + return isBitFieldMember() && bitFieldGroup == null; + } + + private int getDepth() { + int depth = 0; + DefaultCompositeMember p = parent; + while (p != null) { + p = p.parent; + ++depth; + } + return depth; + } + + @Override + public String toString() { + String type; + if (isUnionContainer()) { + type = "Union"; + } + else if (isStructureContainer()) { + type = "Structure"; + } + else if (isBitFieldMember()) { + type = memberDataType.toString(); + } + else { + type = memberDataTypeName; + } + return "[CompositeMember: " + memberOffset + " " + memberName + " " + type + "]"; + } + + /** + * DataTypeResolver provides the ability to resolve a member's data-type + * at the time of construction. + */ + interface DataTypeResolver { + /** + * Find the specified member's data type + * @param member composite member to be resolved + * @return data-type which corresponds to the specified member's data-type name or null + * if unable to resolve. + * @throws CancelledException if operation cancelled + */ + WrappedDataType findDataType(DefaultCompositeMember member) throws CancelledException; + + /** + * Callback to resolve and finalize composite definition. The caller may return immediately + * if composite as previously been resolved as reflected by the composite datatype. + * This callback is necessary to ensure that the composite alignment has been + * established prior to finalizing the compsoite currently being resolved. + * @param memberDefinition member definition object + * @param composite composite member datatype which corresponds to the specified + * memberDefinition. + * @param monitor task monitor + * @throws CancelledException + */ + void resolveComposite(PdbMember compositeDefinition, Composite composite, + TaskMonitor monitor) throws CancelledException; + } + + /** + * Attempt to add a child member to this composite hierarchy + * @param child PDB data-type member record + * @param monitor task monitor + * @return true if child data type resolved and it was successfully added to composite hierarchy, + * false if unable to resolve member's data-type or other error occurred. + * NOTE: there may be complex hierarchies not yet handled. + * @throws CancelledException if operation cancelled + * @throws DataTypeDependencyException if child's datatype can not be resolved. + * It may be possible to skip and continue with next child. + */ + private boolean addMember(PdbMember child, TaskMonitor monitor) + throws CancelledException, DataTypeDependencyException { + + if (!isContainer()) { + throw new AssertException("addMember only permitted on root members"); + } + if (!(memberDataType instanceof Composite)) { + throw new AssertException(); + } + return addMember( + new DefaultCompositeMember(child, dataTypeManager, errorConsumer, monitor)); + } + + private CategoryPath getChildCategoryPath() { + return new CategoryPath(memberDataType.getCategoryPath(), getDataTypeName()); + } + + private String getOutermostDataTypeName() { + if (parent != null) { + return parent.getOutermostDataTypeName(); + } + return getDataTypeName(); + } + + private boolean transformIntoUnionContainer() { + + if (parent == null) { + throw new AssertException(); + } + + if (getDepth() >= MAX_CONSTRUCTION_DEPTH) { + Msg.error(this, "PDB composite reconstruction exceeded maximum allowed depth: " + + getOutermostDataTypeName()); + return false; + } + + // Remove siblings from parent whose offsets are greater + List elderSiblings = kidnapElderSiblingsFromParentStructure(); + + DefaultCompositeMember memberCopy = new DefaultCompositeMember(this); + memberCopy.memberOffset = 0; + + CategoryPath tempCategoryPath = parent.getDataType().getCategoryPath(); + String tempName = allocateTemporaryContainerName("union"); + + Union nestedUnion = new UnionDataType(tempCategoryPath, tempName, dataTypeManager); + + nestedUnion.add(memberDataType, memberName, null); + + String oldName = memberName; + memberName = tempName; + memberDataType = nestedUnion; + memberIsZeroLengthArray = false; + memberDataTypeName = null; // signifies a container + initializeContainer(); + + unionMemberList.add(memberCopy); + memberCopy.parent = this; + + if (!elderSiblings.isEmpty()) { + if (!memberCopy.transformIntoStructureContainer()) { + return false; + } + for (CompositeMember sibling : elderSiblings) { + sibling.setOffset(sibling.getOffset() - memberOffset); + if (!sibling.addToStructure(memberCopy)) { + return false; + } + } + } + + if (parent != null) { + parent.memberChanged(oldName, this); + } + + return true; + } + + @Override + boolean addToStructure(DefaultCompositeMember structure) { + return structure.addStructureMember(this); + } + + boolean transformIntoStructureContainer() { + + if (parent == null) { + throw new AssertException(); + } + + if (getDepth() >= MAX_CONSTRUCTION_DEPTH) { + Msg.error(this, "PDB composite reconstruction exceeded maximum allowed depth: " + + getOutermostDataTypeName()); + return false; + } + + DefaultCompositeMember memberCopy = new DefaultCompositeMember(this); + memberCopy.memberOffset = 0; + + CategoryPath tempCategoryPath = parent.getDataType().getCategoryPath(); + String tempName = allocateTemporaryContainerName("struct"); + + Structure nestedStructure = + new StructureDataType(tempCategoryPath, tempName, 0, dataTypeManager); + + String oldName = memberName; + DataType oldDataType = memberDataType; + String comment = getStructureMemberComment(); + + DefaultCompositeMember deferredBitFieldMember = null; + if (oldDataType instanceof PdbBitField) { + PdbBitField bitfieldDt = (PdbBitField) oldDataType; + try { + int bitOffset = bitfieldDt.getBitOffsetWithinBase(); + DefaultCompositeMember padding = getPaddingBitField(null, memberCopy); + if (padding != null) { + deferredBitFieldMember = memberCopy; + memberCopy = padding; + bitfieldDt = (PdbBitField) memberCopy.memberDataType; + bitOffset = bitfieldDt.getBitOffsetWithinBase(); + } + else if (bitOffset < 0) { + // TODO: assumes little-endian, add support for big-endian + bitOffset = 0; + } + + nestedStructure.insertBitFieldAt(0, bitfieldDt.getBaseTypeSize(), bitOffset, + bitfieldDt.getBaseDataType(), bitfieldDt.getDeclaredBitSize(), + memberCopy.memberName, null); + } + catch (InvalidDataTypeException e) { + Msg.error(this, "PDB failed to add bitfield: " + e.getMessage()); + return false; + } + } + else { + nestedStructure.insertAtOffset(0, oldDataType, oldDataType.getLength(), oldName, + comment); + } + + memberName = tempName; + memberDataType = nestedStructure; + memberIsZeroLengthArray = false; + memberDataTypeName = null; // signifies a container + initializeContainer(); + + structureMemberRangeMap.paintRange(0, memberCopy.getLength() - 1, 0); + structureMemberOffsetMap.put(0, memberCopy); + + memberCopy.setParent(this); + + if (parent != null) { + parent.memberChanged(oldName, this); + } + + if (deferredBitFieldMember != null) { + return addStructureMember(deferredBitFieldMember); + } + return true; + } + + private String getStructureMemberComment() { + if (memberIsZeroLengthArray) { + return "warning: zero length array forced to have one element"; + } + return null; + } + + private boolean isRelatedBitField(int conflictOffset, DefaultCompositeMember newMember) { + if (!isContainer()) { + throw new AssertException(); + } + if (conflictOffset < 0 || !newMember.isBitFieldMember()) { + return false; + } + + CompositeMember conflictMember = structureMemberOffsetMap.get(conflictOffset); + return isRelatedBitField(conflictMember, newMember); + } + + private boolean isRelatedBitField(CompositeMember existingMember, + DefaultCompositeMember newMember) { + + if (!newMember.isBitFieldMember()) { + return false; + } + + if (existingMember == null) { + return false; + } + + if (isUnionContainer() && existingMember.isStructureContainer()) { + DefaultCompositeMember structureMember = (DefaultCompositeMember) existingMember; + return structureMember.isRelatedBitField(newMember.getOffset(), newMember); + } + + if (!existingMember.isBitFieldMember() || + existingMember.getOffset() != newMember.getOffset() || + existingMember.getLength() != newMember.getLength()) { + return false; + } + + // Assume grouped bit-fields are added sequentially + // Unioned bit-fields can not be reliably differentiated from those contained + // within a structure + + Composite composite = (Composite) memberDataType; + DataTypeComponent component = composite.getComponent(composite.getNumComponents() - 1); +// if (component.getOffset() != newMember.getOffset()) { +// return false; // unexpected +// } + + DataType dataType = component.getDataType(); + if (!(dataType instanceof BitFieldDataType) && !(dataType == DataType.DEFAULT)) { + return false; + } + + PdbBitField newBitField = (PdbBitField) newMember.getDataType(); + + // NOTE: assumes little-endian bitfield packing + // TODO: Add support for big-endian + + int consumed; + if (existingMember instanceof BitFieldGroupCompositeMember) { + consumed = ((BitFieldGroupCompositeMember) existingMember).getConsumedBits(); + } + else { + DefaultCompositeMember m = (DefaultCompositeMember) existingMember; + BitFieldDataType conflictBitField = (BitFieldDataType) m.memberDataType; + consumed = conflictBitField.getBitOffset() + conflictBitField.getBitSize(); + } + + int relativeBitOffset = 0; + int bitOffsetWithinBase = newBitField.getBitOffsetWithinBase(); + if (bitOffsetWithinBase >= 0) { + relativeBitOffset = bitOffsetWithinBase - consumed; + if (relativeBitOffset < 0) { + return false; // overlap + } + } + + // ensure that bit fields can get packed together + return (consumed + relativeBitOffset + newBitField.getBitSize()) <= (8 * + newBitField.getBaseTypeSize()); + } + + private DefaultCompositeMember getPaddingBitField(BitFieldGroupCompositeMember bfGroup, + DefaultCompositeMember nextBitFieldMember) throws InvalidDataTypeException { + + if (!nextBitFieldMember.isBitFieldMember()) { + throw new AssertException(); + } + + // NOTE: assumes little-endian bitfield packing + // TODO: Add support for big-endian + + int nextBitOffset = 0; + if (bfGroup != null) { + nextBitOffset = bfGroup.getConsumedBits(); + } + + PdbBitField nextBitfieldDt = (PdbBitField) nextBitFieldMember.getDataType(); + + int bitOffsetWithinBase = nextBitfieldDt.getBitOffsetWithinBase(); + if (bitOffsetWithinBase > nextBitOffset) { + // if bit-offset was specified padding may be required + + int fillerBitSize = bitOffsetWithinBase - nextBitOffset; + // bitOffset = bitOffset; will need adjustment for big-endian + + return new DefaultCompositeMember(nextBitFieldMember.memberOffset, + nextBitfieldDt.getBaseDataType(), fillerBitSize, nextBitOffset); + } + return null; + } + + private boolean addStructureMember(DefaultCompositeMember member) { + try { + // check for conflict within structure container deferred + int conflictOffset = structureMemberRangeMap.getValue(member.memberOffset); + if (conflictOffset < 0) { + + DefaultCompositeMember deferredBitFieldMember = null; + + if (member.isBitFieldMember()) { + + PdbBitField bitfieldDt = (PdbBitField) member.memberDataType; + + int bitOffset = bitfieldDt.getBitOffsetWithinBase(); + DefaultCompositeMember padding = getPaddingBitField(null, member); + if (padding != null) { + deferredBitFieldMember = member; + member = padding; + bitfieldDt = (PdbBitField) member.memberDataType; + bitOffset = bitfieldDt.getBitOffsetWithinBase(); + } + else if (bitOffset < 0) { + // TODO: assumes little-endian, add support for big-endian + bitOffset = 0; + } + + ((Structure) memberDataType).insertBitFieldAt(member.memberOffset, + bitfieldDt.getBaseTypeSize(), bitOffset, bitfieldDt.getBaseDataType(), + bitfieldDt.getDeclaredBitSize(), member.getName(), + member.getStructureMemberComment()); + + } + else { + ((Structure) memberDataType).insertAtOffset(member.memberOffset, + member.memberDataType, member.getLength(), member.memberName, + member.getStructureMemberComment()); + } + + member.parent = this; + structureMemberOffsetMap.put(member.memberOffset, member); + structureMemberRangeMap.paintRange(member.memberOffset, + member.memberOffset + member.getLength() - 1, member.memberOffset); + + if (deferredBitFieldMember != null) { + return addStructureMember(deferredBitFieldMember); + } + + if (parent != null) { + parent.sizeChanged(this); + } + + return true; + } + + CompositeMember conflictMember = structureMemberOffsetMap.get(conflictOffset); + + if (isRelatedBitField(conflictOffset, member)) { + + BitFieldGroupCompositeMember bfGroup; + if (conflictMember instanceof BitFieldGroupCompositeMember) { + bfGroup = (BitFieldGroupCompositeMember) conflictMember; + } + else { + bfGroup = new BitFieldGroupCompositeMember(); + bfGroup.addToGroup(conflictMember); + structureMemberOffsetMap.put(bfGroup.getOffset(), bfGroup); + } + + DefaultCompositeMember deferredBitFieldMember = null; + + PdbBitField bitfieldDt = (PdbBitField) member.memberDataType; + + int bitOffset = bitfieldDt.getBitOffsetWithinBase(); + DefaultCompositeMember padding = getPaddingBitField(bfGroup, member); + if (padding != null) { + deferredBitFieldMember = member; + member = padding; + bitfieldDt = (PdbBitField) member.memberDataType; + bitOffset = bitfieldDt.getBitOffsetWithinBase(); + } + else if (bitOffset < 0) { + // TODO: assumes little-endian, add support for big-endian + bitOffset = bfGroup.getConsumedBits(); + } + + // Error if member and conflict member do not have same offset and type length. + // This assumes bit-field packing does not mix type size together as does gcc + bfGroup.addToGroup(member); + + ((Structure) memberDataType).insertBitFieldAt(member.memberOffset, + bitfieldDt.getBaseTypeSize(), bitOffset, bitfieldDt.getBaseDataType(), + bitfieldDt.getDeclaredBitSize(), member.getName(), + member.getStructureMemberComment()); + + member.parent = this; + + if (deferredBitFieldMember != null) { + return addStructureMember(deferredBitFieldMember); + } + + return true; + } + + // adjust this member's offset for addition to container + member.setOffset(member.getOffset() - conflictMember.getOffset()); + + return conflictMember.addMember(member); + } + catch (InvalidDataTypeException e) { + Msg.error(this, "PDB failed to add bitfield: " + e.getMessage()); + return false; + } + } + + private boolean addUnionMember(DefaultCompositeMember member) { + + if (member.memberOffset == 0) { + + if (unionMemberList.size() != 0 && member.isBitFieldMember()) { + CompositeMember lastUnionMember = unionMemberList.get(unionMemberList.size() - 1); + if (isRelatedBitField(lastUnionMember, member)) { + if (lastUnionMember.isSingleBitFieldMember() && + !((DefaultCompositeMember) lastUnionMember).transformIntoStructureContainer()) { + return false; + } + return lastUnionMember.addMember(member); + } + } + + unionMemberList.add(member); + member.parent = this; + ((Union) memberDataType).add(member.memberDataType, member.memberName, null); + if (parent != null) { + parent.sizeChanged(this); + } + if (member.memberIsZeroLengthArray && !member.transformIntoStructureContainer()) { + return false; + } + return true; + } + + // NOTE: It is assumed that offset will always be ascending and not reach back to union + // members before the last one + + CompositeMember lastUnionMember = unionMemberList.get(unionMemberList.size() - 1); + + if (lastUnionMember.isStructureContainer() && + member.memberOffset >= lastUnionMember.getOffset()) { + DefaultCompositeMember struct = (DefaultCompositeMember) lastUnionMember; + if (struct.isRelatedBitField(member.memberOffset - lastUnionMember.getOffset(), + member)) { + // pack bit-field into member structure with related bit field(s) + member.memberOffset -= lastUnionMember.getOffset(); + return lastUnionMember.addMember(member); + } + } + + if (member.memberOffset >= (lastUnionMember.getOffset() + lastUnionMember.getLength())) { + // NOTE: Placement is rather speculative - assume structure is required + // TODO: watch out for nested union + member.memberOffset -= lastUnionMember.getOffset(); + return lastUnionMember.addMember(member); + } + + // NOTE: union must be forced into structure transformation + if (lastUnionMember instanceof DefaultCompositeMember) { + DefaultCompositeMember m = (DefaultCompositeMember) lastUnionMember; + if (m.isUnionContainer() && !m.transformIntoStructureContainer()) { + return false; + } + } + return lastUnionMember.addMember(member); + } + + private void sizeChanged(DefaultCompositeMember pdbMember) { + if (structureMemberRangeMap != null) { + structureMemberRangeMap.paintRange(pdbMember.memberOffset, + pdbMember.memberOffset + pdbMember.getLength() - 1, pdbMember.memberOffset); + } + if (parent != null) { + parent.sizeChanged(this); + } + } + + private void memberChanged(String fieldName, DefaultCompositeMember newMember) { + if (!newMember.isContainer()) { + throw new AssertException(); + } + if (isUnionContainer()) { + Union union = (Union) memberDataType; + int count = union.getNumComponents(); + for (int i = 0; i < count; i++) { + DataTypeComponent component = union.getComponent(i); + if (fieldName.equals(component.getFieldName())) { + union.delete(i); + union.insert(i, newMember.getDataType(), newMember.getLength(), + newMember.memberName, null); + break; + } + } + } + else if (isStructureContainer()) { + Structure struct = (Structure) memberDataType; + // TODO: complicated by bitfields + struct.deleteAtOffset(newMember.getOffset()); + struct.insertAtOffset(newMember.getOffset(), newMember.getDataType(), + newMember.getLength()); + structureMemberOffsetMap.put(newMember.getOffset(), newMember); + } + } + + private void memberNameChanged(String oldFieldName, String newFieldName) { + if (isContainer()) { + Composite composite = (Composite) memberDataType; + int count = composite.getNumComponents(); + for (int i = 0; i < count; i++) { + DataTypeComponent component = composite.getComponent(i); + if (oldFieldName.equals(component.getFieldName())) { + try { + component.setFieldName(newFieldName); + } + catch (DuplicateNameException e) { + Msg.error(this, "Failed to rename temporary component name: " + + getDataTypeName() + "." + oldFieldName + " -> " + newFieldName); + } + break; + } + } + } + } + + private int getOrdinal(String fieldName) throws NotFoundException { + if (!isContainer()) { + throw new AssertException(); + } + Composite composite = (Composite) memberDataType; + int count = composite.getNumComponents(); + for (int i = 0; i < count; i++) { + DataTypeComponent component = composite.getComponent(i); + if (fieldName.equals(component.getFieldName())) { + return i; + } + } + throw new NotFoundException(); + } + + @Override + boolean addMember(DefaultCompositeMember member) { + + if (member.memberDataType == null || member.memberDataType.getLength() <= 0) { + Msg.debug(this, "Failed to resolve datatype " + member.getDataTypeName()); + return false; + } + + if (!isContainer()) { + if (member.memberOffset != 0) { + if (!transformIntoStructureContainer()) { + return false; + } + } + else { + if (!transformIntoUnionContainer()) { + return false; + } + } + } + + if (isUnionContainer()) { + return addUnionMember(member); + } + return addStructureMember(member); + } + + /** + * This method facilitates the removal and collection of all siblings of this + * member from its parent container. Only those siblings whose offset is greater + * than this member's offset will be included. The use of this method is necessary when + * a member sequence has been added to a structure container and it is later decided to + * push this member and its siblings into a new sub-composite. Before they can be + * added to the new container they must be removed from their current container + * using this method. + * @return list of sibling structure members removed from parent + */ + private List kidnapElderSiblingsFromParentStructure() { + + List list = new ArrayList<>(); + if (parent == null || !parent.isStructureContainer()) { + return list; + } + + Structure parentStruct = (Structure) parent.memberDataType; + + // structureMemberOffsetMap may contain BitFieldGroupCompositeMember which corresponds + // to multiple components within the actual parent structure so there is not a one-to-one + // relationship. + + for (int offset : parent.structureMemberOffsetMap.keySet()) { + CompositeMember m = parent.structureMemberOffsetMap.get(offset); + if (m.getOffset() >= memberOffset && m != this) { + list.add(m); + } + } + + // must remove sibling bit fields at same offset but must leave + // first one behind to facilitate subsequent component swap. + boolean skipIfEqual = true; + for (DataTypeComponent component : parentStruct.getComponents()) { + int offset = component.getOffset(); + if (offset >= memberOffset) { + if (skipIfEqual && offset == memberOffset) { + skipIfEqual = false; + } + else { + parentStruct.clearComponent(component.getOrdinal()); + parent.structureMemberOffsetMap.remove(offset); + } + } + } + + parent.structureMemberRangeMap.paintRange(memberOffset + getLength(), parent.getLength(), + -1); + + return list; + } + + /** + * Buildup an empty composite by applying datatype composite members. + * Only those children with a kind of "Member" will be processed. + * @param composite empty composite to which members will be added + * @param isClass true if composite corresponds to a Class structure, else false + * @param preferredCompositeSize preferred size of composite, <= 0 indicates unknown + * @param members list of composite members + * @param errorConsumer error consumer (may be null) + * @param monitor task monitor + * @return true if members successfully added to composite + * @throws CancelledException if monitor is cancelled + */ + static boolean applyDataTypeMembers(Composite composite, boolean isClass, + int preferredCompositeSize, List members, + Consumer errorConsumer, TaskMonitor monitor) throws CancelledException { + + Composite editComposite = composite; + + DefaultCompositeMember rootMember = + new DefaultCompositeMember(isClass, editComposite, errorConsumer); + + for (PdbMember m : members) { + monitor.checkCanceled(); + try { + if (!rootMember.addMember(m, monitor)) { + return false; + } + } + catch (DataTypeDependencyException e) { + String message = "Failed to resolve datatype dependency for " + + composite.getPathName() + ": " + m.getDataTypeName(); + if (errorConsumer != null) { + errorConsumer.accept(message); + } + else { + Msg.error(DefaultCompositeMember.class, message); + } + } + } + + rootMember.finalizeDataType(preferredCompositeSize); + return true; + } + + void setBitFieldGroup(BitFieldGroupCompositeMember group) { + bitFieldGroup = group; + } + + private static enum MemberType { + + //@formatter:off + STRUCTURE, + UNION, + MEMBER; + //@formatter:on + + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java new file mode 100644 index 0000000000..b392a10386 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultPdbMember.java @@ -0,0 +1,111 @@ +/* ### + * 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.util.bin.format.pdb; + +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.InvalidDataTypeException; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; +import ghidra.util.exception.CancelledException; + +/** + * PdbMember convey PDB member information used for datatype + * reconstruction. The memberDataTypeName is expected to include + * namespace prefixes when relevant. When representing bitfields the + * memberName is used to convey bit-size and bit-offset information + * (e.g., fieldname:SSSS[:XXXX] where SSSS corresponds to the bit-size + * and XXXX corresponds to an optional bit-offset). + */ +public class DefaultPdbMember extends PdbMember { + + private final String name; + + private boolean isBitField; + private int bitFieldSize = -1; + private int bitFieldOffset = -1; + + private final PdbDataTypeParser dataTypeParser; + + /** + * Default PDB member construction + * @param name member field name. For bitfields this also conveys the bit-size + * and optionally the bit-offset. + * @param dataTypeName field datatype or the base datatype associated with a bitfield + * @param offset + * @param dataTypeParser + */ + DefaultPdbMember(String name, String dataTypeName, int offset, + PdbDataTypeParser dataTypeParser) { + super(getMemberName(name), dataTypeName, offset); + this.name = name; + parseBitField(); + this.dataTypeParser = dataTypeParser; + } + + private static String getMemberName(String name) { + int bitFieldColonIndex = name != null ? name.indexOf(':') : -1; + if (bitFieldColonIndex >= 0) { + return name.substring(0, bitFieldColonIndex); + } + return name; + } + + @Override + protected WrappedDataType getDataType() throws CancelledException { + WrappedDataType wrappedDt = dataTypeParser.findDataType(getDataTypeName()); + if (wrappedDt != null && isBitField) { + if (wrappedDt.isZeroLengthArray()) { + return null; + } + PdbBitField bitFieldDt; + try { + DataType baseDataType = + wrappedDt.getDataType().clone(dataTypeParser.getProgramDataTypeManager()); + bitFieldDt = new PdbBitField(baseDataType, bitFieldSize, bitFieldOffset); + } + catch (InvalidDataTypeException e) { + Msg.error(this, "PDB parse error: " + e.getMessage()); + return null; + } + wrappedDt = new WrappedDataType(bitFieldDt, false); + } + return wrappedDt; + } + + private void parseBitField() { + int bitFieldColonIndex = name != null ? name.indexOf(':') : -1; + if (bitFieldColonIndex >= 0) { + + isBitField = true; + + String bitSizeOffsetStr = name.substring(bitFieldColonIndex + 1); + + try { + int colonIndex = bitSizeOffsetStr.indexOf(':'); + if (colonIndex > 0) { + bitFieldOffset = (int) NumericUtilities.parseNumber( + bitSizeOffsetStr.substring(colonIndex + 1)); + bitSizeOffsetStr = bitSizeOffsetStr.substring(0, colonIndex); + } + bitFieldSize = (int) NumericUtilities.parseNumber(bitSizeOffsetStr); + } + catch (NumberFormatException e) { + // ignore + } + } + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java new file mode 100644 index 0000000000..362ba271fd --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java @@ -0,0 +1,83 @@ +/* ### + * 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.util.bin.format.pdb; + +import ghidra.program.model.data.*; +import ghidra.util.exception.AssertException; + +/** + * PdbBitField provides ability to hang onto bitfield as a datatype. + * This will be transformed to a normal BitFieldDataType when cloned. + */ +class PdbBitField extends BitFieldDataType { + + private int bitOffsetWithinBaseType; + + // TODO: add support for big-endian + + /** + * Construct a PDB bitfield (not intended for direct use by DataTypeManager) + * @param baseDataType fielfield base datatype cloned for target datatype manager + * @param bitSize bitfield size in bits + * @param bitOffsetWithinBaseType bit offset within baseDataType or -1 if unknown + * @throws InvalidDataTypeException if invalid bitfield parameters are specified + */ + protected PdbBitField(DataType baseDataType, int bitSize, int bitOffsetWithinBaseType) + throws InvalidDataTypeException { + super(baseDataType, bitSize, + getMinimalBitOffset(baseDataType, bitSize, bitOffsetWithinBaseType), 0); + if (bitSize < 1) { + throw new InvalidDataTypeException("invalid PDB bit size: " + bitSize); + } + if (bitOffsetWithinBaseType < -1) { + throw new InvalidDataTypeException( + "invalid PDB bit offset: " + bitOffsetWithinBaseType); + } + this.bitOffsetWithinBaseType = bitOffsetWithinBaseType; + } + + private static int getMinimalBitOffset(DataType baseDataType, int bitSize, + int bitOffsetWithinBaseType) { + if (bitOffsetWithinBaseType < 0) { + return 0; + } + // assumes little endian packing (lsb first) + return bitOffsetWithinBaseType % 8; + } + + /** + * Get the bit offset within the full base type + * @return base type bit offset or -1 if unknown + */ + public int getBitOffsetWithinBase() { + return bitOffsetWithinBaseType; + } + + @Override + public BitFieldDataType clone(DataTypeManager dtm) { + if (dtm != getDataTypeManager()) { + throw new AssertException("unsupported clone operation"); + } + return this; + } + + @Override + public String toString() { + return getDisplayName() + "(baseSize:" + getBaseTypeSize() + ",bitOffsetInBase:" + + bitOffsetWithinBaseType + ")"; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbDataTypeParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbDataTypeParser.java new file mode 100644 index 0000000000..7af5af3f30 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbDataTypeParser.java @@ -0,0 +1,321 @@ +/* ### + * 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.util.bin.format.pdb; + +import java.util.*; + +import ghidra.app.services.DataTypeManagerService; +import ghidra.program.database.data.DataTypeUtilities; +import ghidra.program.model.data.*; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +class PdbDataTypeParser { + + private final static String NO_TYPE = "NoType"; + + private DataTypeManager programDataTypeMgr; + private DataTypeManagerService service; + private TaskMonitor monitor; + + private Map dataTypeCache = new HashMap<>(); + + PdbDataTypeParser(DataTypeManager programDataTypeMgr, DataTypeManagerService service, + TaskMonitor monitor) { + this.programDataTypeMgr = programDataTypeMgr; + this.service = service; + this.monitor = monitor; + createMandatoryDataTypes(); + } + + private void createMandatoryDataTypes() { + + addDataType(new TypedefDataType("wchar", WideCharDataType.dataType)); + + addDataType(new TypedefDataType("__int8", + AbstractIntegerDataType.getSignedDataType(1, programDataTypeMgr))); + addDataType(new TypedefDataType("__uint8", + AbstractIntegerDataType.getUnsignedDataType(1, programDataTypeMgr))); + + addDataType(new TypedefDataType("__int16", + AbstractIntegerDataType.getSignedDataType(2, programDataTypeMgr))); + addDataType(new TypedefDataType("__uint16", + AbstractIntegerDataType.getUnsignedDataType(2, programDataTypeMgr))); + + addDataType(new TypedefDataType("__int32", + AbstractIntegerDataType.getSignedDataType(4, programDataTypeMgr))); + addDataType(new TypedefDataType("__uint32", + AbstractIntegerDataType.getUnsignedDataType(2, programDataTypeMgr))); + + addDataType(new TypedefDataType("__int64", + AbstractIntegerDataType.getSignedDataType(8, programDataTypeMgr))); + addDataType(new TypedefDataType("__uint64", + AbstractIntegerDataType.getUnsignedDataType(8, programDataTypeMgr))); + } + + /** + * Get the target program's datatype manager + * @return program's datatype manager + */ + public DataTypeManager getProgramDataTypeManager() { + return programDataTypeMgr; + } + + void flushDataTypeCache() { + for (DataType dt : dataTypeCache.values()) { + programDataTypeMgr.resolve(dt, + DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); + } + dataTypeCache.clear(); + } + + /** + * Ensures that the data type managers are used in a particular order. + * The order is as follows: + * 1) the program's data type manager + * 2) the built-in data type manager + * 3) the open data type archives + */ + private class PdbDataTypeManagerComparator implements Comparator { + @Override + public int compare(DataTypeManager dtm1, DataTypeManager dtm2) { + if (dtm1 == programDataTypeMgr) { + return -1; + } + if (dtm2 == programDataTypeMgr) { + return 1; + } + if (dtm1 instanceof BuiltInDataTypeManager) { + return -1; + } + if (dtm2 instanceof BuiltInDataTypeManager) { + return 1; + } + return 0; + } + } + + void clear() { + dataTypeCache.clear(); + } + + DataType getCachedDataType(String key) { + return dataTypeCache.get(key); + } + + void cacheDataType(String key, DataType dataType) { + dataTypeCache.put(key, dataType); + } + + void addDataType(DataType dataType) { + + if (dataType instanceof Composite) { + DataTypeComponent[] components = ((Composite) dataType).getComponents(); + for (DataTypeComponent component : components) { + addDataType(component.getDataType()); + } + } + + ArrayList oldDataTypeList = new ArrayList<>(); + programDataTypeMgr.findDataTypes(dataType.getName(), oldDataTypeList); + dataType = programDataTypeMgr.addDataType(dataType, + DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); + + cacheDataType(dataType.getName(), dataType); + + for (DataType oldDataType : oldDataTypeList) { + if (oldDataType.getLength() == 0 && + oldDataType.getClass().equals(dataType.getClass())) { + try { + programDataTypeMgr.replaceDataType(oldDataType, dataType, false); + } + catch (DataTypeDependencyException e) { + // ignore + } + } + } + } + + private DataType findDataTypeInArchives(String datatype, TaskMonitor monitor) + throws CancelledException { + + DataTypeManager[] managers = service.getDataTypeManagers(); + Arrays.sort(managers, new PdbDataTypeManagerComparator()); + for (DataTypeManager manager : managers) { + if (monitor.isCancelled()) { + throw new CancelledException(); + } + DataType dt = DataTypeUtilities.findNamespaceQualifiedDataType(manager, datatype, null); + if (dt != null) { + cacheDataType(datatype, dt); + return dt; + } + } + return null; + } + + private DataType findBaseDataType(String dataTypeName, TaskMonitor monitor) + throws CancelledException { + DataType dt = getCachedDataType(dataTypeName); + if (dt != null) { + return dt; + } + + // PDP category does not apply to built-ins which always live at the root + + BuiltInDataTypeManager builtInDTM = BuiltInDataTypeManager.getDataTypeManager(); + dt = builtInDTM.getDataType(new DataTypePath(CategoryPath.ROOT, dataTypeName)); + if (dt == null) { + dt = findDataTypeInArchives(dataTypeName, monitor); + } + return dt; + } + + /** + * Find a data-type by name in a case-sensitive manner. + * @param monitor task monitor + * @param dataTypeName data-type name (may be qualified by its namespace) + * @return wrapped data-type or null if not found. + * @throws CancelledException if operation is cancelled + */ + public WrappedDataType findDataType(String datatype) throws CancelledException { + + // NOTE: previous case-insensitive search was removed since type names + // should be case-sensitive + datatype = datatype.trim(); + + if (datatype == null || datatype.length() == 0) { + return null; + } + + if (NO_TYPE.equals(datatype)) { + return new WrappedDataType(VoidDataType.dataType, false); //TODO make it void? + } + + String dataTypeName = datatype; + + // Example type representations: + // char *[2][3] pointer(array(array(char,3),2)) + // char *[2][3] * pointer(array(array(pointer(char),3),2)) + // char [0][2][3] * array(array(array(pointer(char),3),2),0) + // char [2][3] * array(array(pointer(char),3),2) + + int basePointerDepth = 0; + while (dataTypeName.endsWith("*")) { + ++basePointerDepth; + dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim(); + } + + boolean isZeroLengthArray = false; + List arrayDimensions = null; + if (dataTypeName.endsWith("]")) { + arrayDimensions = new ArrayList<>(); + dataTypeName = parseArrayDimensions(dataTypeName, arrayDimensions); + if (dataTypeName == null) { + Msg.error(this, "Failed to parse array dimensions: " + datatype); + return null; + } + isZeroLengthArray = (arrayDimensions.get(arrayDimensions.size() - 1) == 0); + } + + int pointerDepth = 0; + if (arrayDimensions != null) { + while (dataTypeName.endsWith("*")) { + ++pointerDepth; + dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim(); + } + if (pointerDepth != 0 && isZeroLengthArray) { + Msg.error(this, "Unsupported pointer to zero-length array: " + datatype); + return null; + } + } + + // Find base data-type (name may include namespace, e.g., Foo::MyType) + // Primary cache (dataTypeCache) may contain namespace qualified data type + + DataType dt = findBaseDataType(dataTypeName, monitor); + if (dt == null) { + return null; // base type not found + } + + while (basePointerDepth-- != 0) { + dt = createPointer(dt); + } + + if (arrayDimensions != null) { + dt = createArray(dt, arrayDimensions); + } + + while (pointerDepth-- != 0) { + dt = createPointer(dt); + } + + return new WrappedDataType(dt, isZeroLengthArray); + } + + private String parseArrayDimensions(String datatype, List arrayDimensions) { + String dataTypeName = datatype; + boolean zeroLengthArray = false; + while (dataTypeName.endsWith("]")) { + if (zeroLengthArray) { + return null; // only last dimension may be 0 + } + int rBracketPos = dataTypeName.lastIndexOf(']'); + int lBracketPos = dataTypeName.lastIndexOf('['); + if (lBracketPos < 0) { + return null; + } + int dimension; + try { + dimension = Integer.parseInt(dataTypeName.substring(lBracketPos + 1, rBracketPos)); + if (dimension < 0) { + return null; // invalid dimension + } + } + catch (NumberFormatException e) { + return null; + } + dataTypeName = dataTypeName.substring(0, lBracketPos).trim(); + arrayDimensions.add(dimension); + } + return dataTypeName; + } + + DataType createPointer(DataType dt) { + return PointerDataType.getPointer(dt, programDataTypeMgr); + } + + private DataType createArray(DataType dt, List arrayDimensions) { + int dimensionCount = arrayDimensions.size(); + boolean zeroLengthArray = arrayDimensions.get(arrayDimensions.size() - 1) == 0; + if (zeroLengthArray) { + --dimensionCount; + } + for (int i = 0; i < dimensionCount; i++) { + int dimension = arrayDimensions.get(i); + dt = new ArrayDataType(dt, dimension, dt.getLength(), programDataTypeMgr); + } + if (zeroLengthArray) { + // This should be temporary for supported flex-array cases, + // although we do not really support flex-arrays within unions + // or in the middle of structures. + dt = new ArrayDataType(dt, 1, dt.getLength(), programDataTypeMgr); + } + return dt; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java index ac8d88beb5..60ebcd9668 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java @@ -15,31 +15,59 @@ */ package ghidra.app.util.bin.format.pdb; -import ghidra.program.model.symbol.SymbolUtilities; -import ghidra.util.task.TaskMonitor; -import ghidra.util.xml.XmlUtilities; -import ghidra.xml.XmlElement; -import ghidra.xml.XmlTreeNode; +import ghidra.util.exception.CancelledException; + +/** + * PdbMember convey PDB member information used for datatype + * reconstruction. + */ +abstract class PdbMember { -final class PdbMember { final String memberName; final String memberDataTypeName; final int memberOffset; - final String memberKind; - final int memberLength; - PdbMember(XmlTreeNode node, TaskMonitor monitor) { - this(node.getStartElement(), monitor); + protected PdbMember(String memberName, String memberDataTypeName, int memberOffset) { + this.memberName = memberName; + this.memberDataTypeName = memberDataTypeName; + this.memberOffset = memberOffset; } - PdbMember(XmlElement element, TaskMonitor monitor) { - // TODO: Need to examine consistency of renaming names/data types for space removal across - // all of PDB. - memberName = SymbolUtilities.replaceInvalidChars(element.getAttribute("name"), false); - memberDataTypeName = element.getAttribute("datatype"); - memberOffset = XmlUtilities.parseInt(element.getAttribute("offset")); - memberKind = element.getAttribute("kind"); - memberLength = XmlUtilities.parseInt(element.getAttribute("length")); + /** + * Get the member's name which will correspond to the field name. + * @return member field name + */ + public String getName() { + return memberName; } + /** + * Get the member's datatype name (may be namespace qualified) + * @return member's datatype name + */ + public String getDataTypeName() { + return memberDataTypeName; + } + + /** + * Get the member's byte offset within the root composite. + * @return member's byte offset + */ + public int getOffset() { + return memberOffset; + } + + /** + * Get this member's associated data type which has already been cloned for the + * target program's data type manager. This indicates a dependency callback + * and may be used to trigger resolution for composites. When resolving dependencies + * care must be take to avoid circular dependencies which could occur under certain + * error conditions. + * @param member composite member to be resolved + * @return data-type which corresponds to the specified member's data-type name or null + * if unable to resolve. + * @throws CancelledException if operation cancelled + */ + protected abstract WrappedDataType getDataType() throws CancelledException; + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParserNEW.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParserNEW.java index 6adbba1967..1706ec1138 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParserNEW.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParserNEW.java @@ -30,7 +30,6 @@ import ghidra.app.util.importer.LibrarySearchPathManager; import ghidra.app.util.importer.MessageLog; import ghidra.framework.*; import ghidra.framework.options.Options; -import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; @@ -38,9 +37,9 @@ import ghidra.program.model.mem.DumbMemBufferImpl; import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.SystemUtilities; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.InvalidInputException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; +import ghidra.util.xml.XmlUtilities; import ghidra.xml.*; /** @@ -55,8 +54,6 @@ public class PdbParserNEW { public final static File SPECIAL_PDB_LOCATION = new File("C:/WINDOWS/Symbols"); public final static String PDB_STORAGE_PROPERTY = "PDB Storage Directory"; - private final static String NO_TYPE = "NoType"; - static final String STRUCTURE_KIND = "Structure"; static final String UNION_KIND = "Union"; @@ -72,13 +69,15 @@ public class PdbParserNEW { } } + private TaskMonitor monitor; + private final boolean forceAnalysis; private final File pdbFile; private final boolean isXML; private final Program program; + private DataTypeManager dataMgr; private final DataTypeManagerService service; private final PdbProgramAttributes programAttributes; - private Process process; private XmlPullParser parser; private PdbErrorHandler errHandler; @@ -93,23 +92,24 @@ public class PdbParserNEW { * by the PDB to be available within the dataTypeCache using namespace-based type * names. */ - private Map dataTypeCache = new HashMap<>(); + private PdbDataTypeParser dataTypeParser; private Map namespaceMap = new TreeMap<>(); // false: simple namespace, true: class namespace public PdbParserNEW(File pdbFile, Program program, DataTypeManagerService service, - boolean forceAnalysis) { - this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis); + boolean forceAnalysis, TaskMonitor monitor) { + this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis, monitor); } public PdbParserNEW(File pdbFile, Program program, DataTypeManagerService service, - PdbProgramAttributes programAttributes, boolean forceAnalysis) { + PdbProgramAttributes programAttributes, boolean forceAnalysis, TaskMonitor monitor) { this.pdbFile = pdbFile; this.categoryPrefix = "/" + pdbFile.getName(); this.pdbCategory = new CategoryPath(categoryPrefix); this.program = program; + this.dataMgr = program.getDataTypeManager(); this.service = service; this.forceAnalysis = forceAnalysis; - + this.monitor = monitor != null ? monitor : TaskMonitor.DUMMY; this.isXML = pdbFile.getAbsolutePath().endsWith(PdbFileType.XML.toString()); this.programAttributes = programAttributes; } @@ -119,7 +119,7 @@ public class PdbParserNEW { * @return data type manager */ DataTypeManager getProgramDataTypeManager() { - return program.getDataTypeManager(); + return dataMgr; } /** @@ -273,36 +273,33 @@ public class PdbParserNEW { } private void completeDefferedTypeParsing(ApplyDataTypes applyDataTypes, - ApplyDataTypes applyClasses, ApplyTypeDefs applyTypeDefs, TaskMonitor monitor, - MessageLog log) throws CancelledException { + ApplyTypeDefs applyTypeDefs, MessageLog log) throws CancelledException { defineClasses(monitor, log); if (applyDataTypes != null) { applyDataTypes.buildDataTypes(monitor); } - if (applyClasses != null) { - applyClasses.buildDataTypes(monitor); - } + if (applyTypeDefs != null) { - applyTypeDefs.buildTypeDefs(monitor); + applyTypeDefs.buildTypeDefs(monitor); // TODO: no dependencies exit on TypeDefs (use single pass) } // Ensure that all data types are resolved - flushDataTypeCache(); + if (dataTypeParser != null) { + dataTypeParser.flushDataTypeCache(); + } } /** * Apply PDB debug information to the current program * - * @param monitor Monitor used to cancel processing * @param log MessageLog used to record errors * @throws IOException if an error occurs during parsing * @throws PdbException if PDB file has already been loaded * @throws CancelledException if user cancels the current action */ - public void applyTo(TaskMonitor monitor, MessageLog log) - throws IOException, PdbException, CancelledException { + public void applyTo(MessageLog log) throws IOException, PdbException, CancelledException { if (!parsed) { throw new IOException("PDB: parse() must be called before applyTo()"); } @@ -316,10 +313,7 @@ public class PdbParserNEW { Msg.debug(this, "Found PDB for " + program.getName()); try { - PdbUtil.createMandatoryDataTypes(this, monitor); - ApplyDataTypes applyDataTypes = null; - ApplyDataTypes applyClasses = null; ApplyTypeDefs applyTypeDefs = null; boolean typesFlushed = false; @@ -351,10 +345,16 @@ public class PdbParserNEW { ApplyEnums.applyTo(parser, this, monitor, log); } else if (element.getName().equals("datatypes")) { - applyDataTypes = new ApplyDataTypes(this, parser, false, monitor, log); + if (applyDataTypes == null) { + applyDataTypes = new ApplyDataTypes(this, log); + } + applyDataTypes.preProcessDataTypeList(parser, false, monitor); } else if (element.getName().equals("classes")) { - applyClasses = new ApplyDataTypes(this, parser, true, monitor, log); + if (applyDataTypes == null) { + applyDataTypes = new ApplyDataTypes(this, log); + } + applyDataTypes.preProcessDataTypeList(parser, true, monitor); } else if (element.getName().equals("typedefs")) { applyTypeDefs = new ApplyTypeDefs(this, parser, monitor, log); @@ -362,8 +362,7 @@ public class PdbParserNEW { else if (element.getName().equals("functions")) { // apply functions (must occur within XML after all type sections) if (!typesFlushed) { - completeDefferedTypeParsing(applyDataTypes, applyClasses, applyTypeDefs, - monitor, log); + completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log); typesFlushed = true; } ApplyFunctions.applyTo(this, parser, monitor, log); @@ -371,8 +370,7 @@ public class PdbParserNEW { else if (element.getName().equals("tables")) { // apply tables (must occur within XML after all other sections) if (!typesFlushed) { - completeDefferedTypeParsing(applyDataTypes, applyClasses, applyTypeDefs, - monitor, log); + completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log); typesFlushed = true; } ApplyTables.applyTo(this, parser, monitor, log); @@ -383,8 +381,7 @@ public class PdbParserNEW { } if (!typesFlushed) { - completeDefferedTypeParsing(applyDataTypes, applyClasses, applyTypeDefs, monitor, - log); + completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log); } Options options = program.getOptions(Program.PROGRAM_INFO); @@ -413,15 +410,6 @@ public class PdbParserNEW { } } - private void flushDataTypeCache() { - DataTypeManager dtm = program.getDataTypeManager(); - for (DataType dt : dataTypeCache.values()) { - dtm.resolve(dt, - DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); - } - dataTypeCache.clear(); - } - void predefineClass(String classname) { SymbolPath classPath = new SymbolPath(classname); namespaceMap.put(classPath, true); @@ -701,47 +689,47 @@ public class PdbParserNEW { } //errHandler = null; //thread = null; - dataTypeCache.clear(); + if (dataTypeParser != null) { + dataTypeParser.clear(); + } } - boolean isCorrectKind(DataType dt, String kind) { - if (STRUCTURE_KIND.equals(kind)) { + boolean isCorrectKind(DataType dt, PdbXmlKind kind) { + if (kind == PdbXmlKind.STRUCTURE) { return (dt instanceof Structure); } - else if (UNION_KIND.equals(kind)) { + else if (kind == PdbXmlKind.UNION) { return (dt instanceof Union); } return false; } - Composite createComposite(String kind, String name) { - if (STRUCTURE_KIND.equals(kind)) { + Composite createComposite(PdbXmlKind kind, String name) { + if (kind == PdbXmlKind.STRUCTURE) { return createStructure(name, 0); } - else if (UNION_KIND.equals(kind)) { + else if (kind == PdbXmlKind.UNION) { return createUnion(name); } - return null; + throw new IllegalArgumentException("unsupported kind: " + kind); } Structure createStructure(String name, int length) { return new StructureDataType(getCategory(name, true), stripNamespace(name), length, - program.getDataTypeManager()); + dataMgr); } Union createUnion(String name) { - return new UnionDataType(getCategory(name, true), stripNamespace(name), - program.getDataTypeManager()); + return new UnionDataType(getCategory(name, true), stripNamespace(name), dataMgr); } TypedefDataType createTypeDef(String name, DataType baseDataType) { return new TypedefDataType(getCategory(name, true), stripNamespace(name), baseDataType, - program.getDataTypeManager()); + dataMgr); } EnumDataType createEnum(String name, int length) { - return new EnumDataType(getCategory(name, true), stripNamespace(name), length, - program.getDataTypeManager()); + return new EnumDataType(getCategory(name, true), stripNamespace(name), length, dataMgr); } void createString(boolean isUnicode, Address address, MessageLog log, TaskMonitor monitor) { @@ -751,15 +739,15 @@ public class PdbParserNEW { void createData(Address address, String datatype, MessageLog log, TaskMonitor monitor) throws CancelledException { - WrappedDataType wrappedDt = findDataType(datatype, monitor); + WrappedDataType wrappedDt = getDataTypeParser().findDataType(datatype); if (wrappedDt == null) { log.appendMsg("Error: Failed to resolve datatype " + datatype + " at " + address); } - else if (wrappedDt.isZeroLengthArray) { + else if (wrappedDt.isZeroLengthArray()) { Msg.debug(this, "Did not apply zero length array data " + datatype + " at " + address); } else { - createData(address, wrappedDt.dataType, log, monitor); + createData(address, wrappedDt.getDataType(), log, monitor); } } @@ -818,15 +806,15 @@ public class PdbParserNEW { return; } } + Listing listing = program.getListing(); if (existingData == null) { try { - program.getListing().clearCodeUnits(address, address.add(dataTypeLength - 1), - false); + listing.clearCodeUnits(address, address.add(dataTypeLength - 1), false); if (dataType.getLength() == -1) { - program.getListing().createData(address, dataType, dataTypeLength); + listing.createData(address, dataType, dataTypeLength); } else { - program.getListing().createData(address, dataType); + listing.createData(address, dataType); } } catch (Exception e) { @@ -836,9 +824,8 @@ public class PdbParserNEW { } else if (isDataReplaceable(existingData)) { try { - program.getListing().clearCodeUnits(address, address.add(dataTypeLength - 1), - false); - program.getListing().createData(address, dataType, dataTypeLength); + listing.clearCodeUnits(address, address.add(dataTypeLength - 1), false); + listing.createData(address, dataType, dataTypeLength); } catch (Exception e) { log.appendMsg("Unable to replace " + dataType.getDisplayName() + " at 0x" + @@ -960,245 +947,17 @@ public class PdbParserNEW { return false; } - DataType createPointer(DataType dt) { - return PointerDataType.getPointer(dt, program.getDataTypeManager()); - } +// DataType createPointer(DataType dt) { +// return PointerDataType.getPointer(dt, program.getDataTypeManager()); +// } - /** - * Find a data-type by name in a case-sensitive manner. - * @param monitor task monitor - * @param dataTypeName data-type name (may be qualified by its namespace) - * @return wrapped data-type or null if not found. - * @throws CancelledException if operation is cancelled - */ - WrappedDataType findDataType(String datatype, TaskMonitor monitor) throws CancelledException { - - // NOTE: previous case-insensitive search was removed since type names - // should be case-sensitive - datatype = datatype.trim(); - - if (datatype == null || datatype.length() == 0) { - return null; - } - - if (NO_TYPE.equals(datatype)) { - return new WrappedDataType(VoidDataType.dataType, false); //TODO make it void? - } - - String dataTypeName = datatype; - - // Example type representations: - // char *[2][3] pointer(array(array(char,3),2)) - // char *[2][3] * pointer(array(array(pointer(char),3),2)) - // char [0][2][3] * array(array(array(pointer(char),3),2),0) - // char [2][3] * array(array(pointer(char),3),2) - - int basePointerDepth = 0; - while (dataTypeName.endsWith("*")) { - ++basePointerDepth; - dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim(); - } - - boolean isZeroLengthArray = false; - List arrayDimensions = null; - if (dataTypeName.endsWith("]")) { - arrayDimensions = new ArrayList<>(); - dataTypeName = parseArrayDimensions(dataTypeName, arrayDimensions); - if (dataTypeName == null) { - Msg.error(this, "Failed to parse array dimensions: " + datatype); - return null; - } - isZeroLengthArray = (arrayDimensions.get(arrayDimensions.size() - 1) == 0); - } - - int pointerDepth = 0; - if (arrayDimensions != null) { - while (dataTypeName.endsWith("*")) { - ++pointerDepth; - dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim(); - } - if (pointerDepth != 0 && isZeroLengthArray) { - Msg.error(this, "Unsupported pointer to zero-length array: " + datatype); - return null; - } - } - - // Find base data-type (name may include namespace, e.g., Foo::MyType) - // Primary cache (dataTypeCache) may contain namespace qualified data type - - DataType dt = findBaseDataType(dataTypeName, monitor); - if (dt == null) { - return null; // base type not found - } - - while (basePointerDepth-- != 0) { - dt = createPointer(dt); - } - - if (arrayDimensions != null) { - dt = createArray(dt, arrayDimensions); - } - - while (pointerDepth-- != 0) { - dt = createPointer(dt); - } - - return new WrappedDataType(dt, isZeroLengthArray); - } - - private DataType findBaseDataType(String dataTypeName, TaskMonitor monitor) - throws CancelledException { - DataType dt = getCachedDataType(dataTypeName); - if (dt != null) { - return dt; - } - - // PDP category does not apply to built-ins which always live at the root - - BuiltInDataTypeManager builtInDTM = BuiltInDataTypeManager.getDataTypeManager(); - dt = builtInDTM.getDataType(new DataTypePath(CategoryPath.ROOT, dataTypeName)); - if (dt == null) { - dt = findDataTypeInArchives(dataTypeName, monitor); - } - return dt; - } - - static class WrappedDataType { - final boolean isZeroLengthArray; - final DataType dataType; - - WrappedDataType(DataType dataType, boolean isZeroLengthArray) { - this.dataType = dataType; - this.isZeroLengthArray = isZeroLengthArray; - } - } - - private DataType createArray(DataType dt, List arrayDimensions) { - int dimensionCount = arrayDimensions.size(); - boolean zeroLengthArray = arrayDimensions.get(arrayDimensions.size() - 1) == 0; - if (zeroLengthArray) { - --dimensionCount; - } - for (int i = 0; i < dimensionCount; i++) { - int dimension = arrayDimensions.get(i); - dt = new ArrayDataType(dt, dimension, dt.getLength(), program.getDataTypeManager()); - } - if (zeroLengthArray) { - // TODO: improve support for representing zero-length arrays - dt = new ArrayDataType(dt, 1, dt.getLength(), program.getDataTypeManager()); - } - return dt; - } - - private String parseArrayDimensions(String datatype, List arrayDimensions) { - String dataTypeName = datatype; - boolean zeroLengthArray = false; - while (dataTypeName.endsWith("]")) { - if (zeroLengthArray) { - return null; // only last dimension may be 0 - } - int rBracketPos = dataTypeName.lastIndexOf(']'); - int lBracketPos = dataTypeName.lastIndexOf('['); - if (lBracketPos < 0) { - return null; - } - int dimension; - try { - dimension = Integer.parseInt(dataTypeName.substring(lBracketPos + 1, rBracketPos)); - if (dimension < 0) { - return null; // invalid dimension - } - } - catch (NumberFormatException e) { - return null; - } - dataTypeName = dataTypeName.substring(0, lBracketPos).trim(); - arrayDimensions.add(dimension); - } - return dataTypeName; - } - - private DataType findDataTypeInArchives(String datatype, TaskMonitor monitor) - throws CancelledException { - - DataTypeManager[] managers = service.getDataTypeManagers(); - Arrays.sort(managers, new PdbDataTypeManagerComparator()); - for (DataTypeManager manager : managers) { - if (monitor.isCancelled()) { - throw new CancelledException(); - } - DataType dt = DataTypeUtilities.findNamespaceQualifiedDataType(manager, datatype, null); - if (dt != null) { - cacheDataType(datatype, dt); - return dt; - } - } - return null; - } - - /** - * Ensures that the data type managers are used in a particular order. - * The order is as follows: - * 1) the program's data type manager - * 2) the built-in data type manager - * 3) the open data type archives - */ - private class PdbDataTypeManagerComparator implements Comparator { - @Override - public int compare(DataTypeManager dtm1, DataTypeManager dtm2) { - if (dtm1 == program.getDataTypeManager()) { - return -1; - } - if (dtm2 == program.getDataTypeManager()) { - return 1; - } - if (dtm1 instanceof BuiltInDataTypeManager) { - return -1; - } - if (dtm2 instanceof BuiltInDataTypeManager) { - return 1; - } - return 0; - } - } - - DataType getCachedDataType(String key) { - return dataTypeCache.get(key); - } - - void cacheDataType(String key, DataType dataType) { - dataTypeCache.put(key, dataType); - } - - void addDataType(DataType dataType) { - - if (dataType instanceof Composite) { - DataTypeComponent[] components = ((Composite) dataType).getComponents(); - for (DataTypeComponent component : components) { - addDataType(component.getDataType()); - } - } - - DataTypeManager dataTypeMgr = program.getDataTypeManager(); - ArrayList oldDataTypeList = new ArrayList<>(); - dataTypeMgr.findDataTypes(dataType.getName(), oldDataTypeList); - dataType = dataTypeMgr.addDataType(dataType, - DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); - - cacheDataType(dataType.getName(), dataType); - - for (DataType oldDataType : oldDataTypeList) { - if (oldDataType.getLength() == 0 && - oldDataType.getClass().equals(dataType.getClass())) { - try { - dataTypeMgr.replaceDataType(oldDataType, dataType, false); - } - catch (DataTypeDependencyException e) { - // ignore - } - } - } - } +// DataType getCachedDataType(String key) { +// return dataTypeCache.get(key); +// } +// +// void cacheDataType(String key, DataType dataType) { +// dataTypeCache.put(key, dataType); +// } /** * Get the PDB root category path @@ -1540,4 +1299,64 @@ public class PdbParserNEW { return null; } + PdbDataTypeParser getDataTypeParser() { + if (program == null) { + throw new AssertException("Parser was not constructed with program"); + } + if (dataTypeParser == null) { + dataTypeParser = new PdbDataTypeParser(program.getDataTypeManager(), service, monitor); + } + return dataTypeParser; + } + + void cacheDataType(String name, DataType dataType) { + getDataTypeParser().cacheDataType(name, dataType); + } + + DataType getCachedDataType(String name) { + return getDataTypeParser().getCachedDataType(name); + } + + void addDataType(DataType dataType) { + getDataTypeParser().addDataType(dataType); + } + + WrappedDataType findDataType(String dataTypeName) throws CancelledException { + return getDataTypeParser().findDataType(dataTypeName); + } + + public PdbMember getPdbXmlMember(XmlTreeNode node) { + return new PdbXmlMember(node); + } + + public PdbXmlMember getPdbXmlMember(XmlElement element) { + return new PdbXmlMember(element); + } + + class PdbXmlMember extends DefaultPdbMember { + + final PdbXmlKind kind; + + PdbXmlMember(XmlTreeNode node) { + this(node.getStartElement()); + } + + PdbXmlMember(XmlElement element) { + super(SymbolUtilities.replaceInvalidChars(element.getAttribute("name"), false), + element.getAttribute("datatype"), + XmlUtilities.parseInt(element.getAttribute("offset")), dataTypeParser); + kind = PdbXmlKind.parse(element.getAttribute("kind")); + } + + /** + * Kind of member record. Only those records with a Member kind + * are currently considered for inclusion within a composite. + * @return PDB kind + */ + public PdbXmlKind getKind() { + return kind; + } + + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java index d2bbea307f..595aab9d2a 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java @@ -18,13 +18,12 @@ package ghidra.app.util.bin.format.pdb; import ghidra.app.cmd.comments.SetCommentsCmd; import ghidra.app.util.PseudoDisassembler; import ghidra.app.util.PseudoInstruction; -import ghidra.app.util.importer.MessageLog; import ghidra.program.model.address.Address; -import ghidra.program.model.data.*; +import ghidra.program.model.data.Composite; +import ghidra.program.model.data.Structure; import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.Program; import ghidra.util.Conv; -import ghidra.util.task.TaskMonitor; final class PdbUtil { @@ -95,31 +94,31 @@ final class PdbUtil { return false; } - final static void ensureSize(int expectedLength, Composite composite, MessageLog log) { - int actualLength = composite.getLength(); - if (actualLength < expectedLength) { - - composite.setInternallyAligned(false); - if (composite instanceof Structure) { - Structure struct = (Structure) composite; - // if this is an empty structure, the structure will lie to us - // and say it has one element so add 1 to growth factor - struct.growStructure( - expectedLength - actualLength + (struct.isNotYetDefined() ? 1 : 0)); - } - // must be a union data type - else { - DataType datatype = new ArrayDataType(DataType.DEFAULT, expectedLength, - DataType.DEFAULT.getLength()); - composite.add(datatype); - } - } - else if (actualLength > expectedLength) { - log.appendMsg("Warning: Composite data type generated from PDB has size mismatch. " + - composite.getName() + ": expected 0x" + Integer.toHexString(expectedLength) + - ", but was 0x" + Integer.toHexString(actualLength)); - } - } +// final static void ensureSize(int expectedLength, Composite composite, MessageLog log) { +// int actualLength = composite.getLength(); +// if (actualLength < expectedLength) { +// +// composite.setInternallyAligned(false); +// if (composite instanceof Structure) { +// Structure struct = (Structure) composite; +// // if this is an empty structure, the structure will lie to us +// // and say it has one element so add 1 to growth factor +// struct.growStructure( +// expectedLength - actualLength + (struct.isNotYetDefined() ? 1 : 0)); +// } +// // must be a union data type +// else { +// DataType datatype = new ArrayDataType(DataType.DEFAULT, expectedLength, +// DataType.DEFAULT.getLength()); +// composite.add(datatype); +// } +// } +// else if (actualLength > expectedLength) { +// log.appendMsg("Warning: Composite data type generated from PDB has size mismatch. " + +// composite.getName() + ": expected 0x" + Integer.toHexString(expectedLength) + +// ", but was 0x" + Integer.toHexString(actualLength)); +// } +// } final static void clearComponents(Composite composite) { if (composite instanceof Structure) { @@ -160,31 +159,4 @@ final class PdbUtil { return pass + "th pass"; } - final static void createMandatoryDataTypes(PdbParserNEW parser, TaskMonitor monitor) { - - DataTypeManager dtm = parser.getProgramDataTypeManager(); - - parser.addDataType(new TypedefDataType("wchar", WideCharDataType.dataType)); - - parser.addDataType( - new TypedefDataType("__int8", AbstractIntegerDataType.getSignedDataType(1, dtm))); - parser.addDataType( - new TypedefDataType("__uint8", AbstractIntegerDataType.getUnsignedDataType(1, dtm))); - - parser.addDataType( - new TypedefDataType("__int16", AbstractIntegerDataType.getSignedDataType(2, dtm))); - parser.addDataType( - new TypedefDataType("__uint16", AbstractIntegerDataType.getUnsignedDataType(2, dtm))); - - parser.addDataType( - new TypedefDataType("__int32", AbstractIntegerDataType.getSignedDataType(4, dtm))); - parser.addDataType( - new TypedefDataType("__uint32", AbstractIntegerDataType.getUnsignedDataType(2, dtm))); - - parser.addDataType( - new TypedefDataType("__int64", AbstractIntegerDataType.getSignedDataType(8, dtm))); - parser.addDataType( - new TypedefDataType("__uint64", AbstractIntegerDataType.getUnsignedDataType(8, dtm))); - } - } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbXmlKind.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbXmlKind.java new file mode 100644 index 0000000000..441113f008 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbXmlKind.java @@ -0,0 +1,78 @@ +/* ### + * 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.util.bin.format.pdb; + +public enum PdbXmlKind { + + //@formatter:off + STRUCTURE, + UNION, + MEMBER, + STATIC_LOCAL, + OBJECT_POINTER, + PARAMETER, + LOCAL, + UNKNOWN; + //@formatter:on + + private final String camelName; + + private PdbXmlKind() { + camelName = toCamel(name()); + } + + /** + * Get the name in camel form + * @return name in camel form + */ + public String getCamelName() { + return camelName; + } + + private static String toCamel(String name) { + StringBuilder buf = new StringBuilder(); + boolean makeUpper = true; + for (char c : name.toCharArray()) { + if (c == '_') { + makeUpper = true; + continue; + } + if (makeUpper) { + c = Character.toUpperCase(c); + } + buf.append(c); + } + return buf.toString(); + } + + /** + * Parse case-insensitive kind string and return corresponding PdbKind. + * It is expected that kind strings will be camel notation (e.g., OBJECT_POINTER + * kind string would be ObjectPointer). + * If not identified UNKNOWN will be returned. + * @param kind kind string (underscores not permitted) + * @return PdbKind + */ + public static PdbXmlKind parse(String kind) { + for (PdbXmlKind pdbKind : values()) { + if (pdbKind.camelName.equalsIgnoreCase(kind)) { + return pdbKind; + } + } + return UNKNOWN; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java new file mode 100644 index 0000000000..b6bd9d5ae1 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java @@ -0,0 +1,60 @@ +/* ### + * 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.util.bin.format.pdb; + +import ghidra.program.model.data.DataType; + +/** + * WrappedDataType provide the ability to wrap + * a {@link DataType} with additional information not conveyed + * by the datatype on its' own. + *

+ * Note that a BitFieldDataType instance may be specified as the datatype + * in order to convey bitfield related information. + */ +public class WrappedDataType { + + private final boolean isZeroLengthArray; + private final DataType dataType; + + /** + * Constructed wrapped datatype + * @param dataType datatype + * @param isZeroLengthArray true if datatype corresponds to a zero-length + * array which can not directly be represented as an Array datatype, + * else false for all other cases. + */ + protected WrappedDataType(DataType dataType, boolean isZeroLengthArray) { + this.dataType = dataType; + this.isZeroLengthArray = isZeroLengthArray; + } + + /** + * @return datatype + */ + public DataType getDataType() { + return dataType; + } + + /** + * @return true if datatype corresponds to a zero-length array + * which can not directly be represented as an Array datatype, + * else false for all other cases. + */ + public boolean isZeroLengthArray() { + return isZeroLengthArray; + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java index ffd81c7baa..6bbdcf1955 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java @@ -15,11 +15,13 @@ */ package pdb; +import java.io.File; import java.lang.reflect.InvocationTargetException; import docking.DockingWindowManager; import docking.widgets.dialogs.MultiLineMessageDialog; import ghidra.app.plugin.core.analysis.*; +import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.bin.format.pdb.PdbException; import ghidra.app.util.bin.format.pdb.PdbParserNEW; import ghidra.app.util.importer.MessageLog; @@ -32,13 +34,15 @@ import ghidra.util.task.Task; import ghidra.util.task.TaskMonitor; class LoadPdbTask extends Task { - private PdbParserNEW parser; + private File pdbFile; + private DataTypeManagerService service; private final Program program; - LoadPdbTask(Program program, PdbParserNEW parser) { + LoadPdbTask(Program program, File pdbFile, DataTypeManagerService service) { super("Loading PDB...", true, false, false); this.program = program; - this.parser = parser; + this.pdbFile = pdbFile; + this.service = service; } @Override @@ -56,9 +60,12 @@ class LoadPdbTask extends Task { public boolean analysisWorkerCallback(Program currentProgram, Object workerContext, TaskMonitor currentMonitor) throws Exception, CancelledException, PdbException { + PdbParserNEW parser = + new PdbParserNEW(pdbFile, program, service, true, currentMonitor); + parser.parse(); parser.openDataTypeArchives(); - parser.applyTo(currentMonitor, log); + parser.applyTo(log); analyzeSymbols(currentMonitor, log); return !monitor.isCancelled(); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java index daf368ce39..5d6109e4f2 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java @@ -103,10 +103,7 @@ public class PdbPlugin extends Plugin { return; } - // Note: use other constructor if we want to enforce Auto-Analysis before - // applying PDB. - final PdbParserNEW parser = new PdbParserNEW(pdb, program, service, true); - TaskLauncher.launch(new LoadPdbTask(program, parser)); + TaskLauncher.launch(new LoadPdbTask(program, pdb, service)); } catch (Exception pe) { Msg.showError(getClass(), null, "Error", pe.getMessage()); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java index f1a58b9275..217e2b6051 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java @@ -773,11 +773,7 @@ public class PdbSymbolServerPlugin extends Plugin { return; } - // Note: use other constructor if we want to enforce Auto-Analysis before - // applying PDB. - final PdbParserNEW parser = - new PdbParserNEW(downloadedPdb, currentProgram, service, true); - TaskLauncher.launch(new LoadPdbTask(currentProgram, parser)); + TaskLauncher.launch(new LoadPdbTask(currentProgram, downloadedPdb, service)); } catch (Exception pe) { Msg.showError(getClass(), null, "Error", pe.getMessage()); diff --git a/Ghidra/Features/PDB/src/pdb/cpp/symbol.cpp b/Ghidra/Features/PDB/src/pdb/cpp/symbol.cpp index 7a7ce14183..09ca88dac5 100644 --- a/Ghidra/Features/PDB/src/pdb/cpp/symbol.cpp +++ b/Ghidra/Features/PDB/src/pdb/cpp/symbol.cpp @@ -195,12 +195,15 @@ std::wstring getName(IDiaSymbol& symbol) { DWORD locType = 0; ULONGLONG len = 0; + DWORD bitPos = 0; if (symbol.get_locationType(&locType) == S_OK && locType == LocIsBitField && - symbol.get_length(&len) == S_OK) { - const size_t length = wstrName.length() + 4 + 32; // length of: name + ":0x\0" + wag_hex_numeric_str_len + symbol.get_length(&len) == S_OK && + symbol.get_bitPosition(&bitPos) == S_OK) { + // allocate length of: name + ":0x" + len + ":0x" + bitPos + "\0" + const size_t length = wstrName.length() + 70; std::vector str(length); - swprintf_s(str.data(), length, L"%ws:0x%I64x", wstrName.c_str(), len); + swprintf_s(str.data(), length, L"%ws:0x%I64x:0x%x", wstrName.c_str(), len, bitPos); return escapeXmlEntities(str.data()); } diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java new file mode 100644 index 0000000000..8edce05304 --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java @@ -0,0 +1,783 @@ +/* ### + * 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.util.bin.format.pdb; + +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; + +import org.junit.Before; +import org.junit.Test; + +import ghidra.program.model.data.*; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; +import util.CollectionUtils; + +public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest + implements Consumer { + + DataTypeManager dataMgr; + PdbDataTypeParser dataTypeParser; + + @Before + public void setUp() { + // DataOrganization based on x86-64.cspec + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + dataOrg.setBigEndian(false); + dataOrg.setAbsoluteMaxAlignment(0); + dataOrg.setMachineAlignment(2); + dataOrg.setDefaultPointerAlignment(8); + dataOrg.setPointerSize(8); + + dataOrg.setSizeAlignment(8, 8); + + BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); + bitFieldPacking.setUseMSConvention(true); + dataOrg.setBitFieldPacking(bitFieldPacking); + + dataMgr = new TestDummyDataTypeManager() { + HashMap dataTypeMap = new HashMap<>(); + + @Override + public DataOrganization getDataOrganization() { + return dataOrg; + } + + @Override + public DataType addDataType(DataType dataType, DataTypeConflictHandler handler) { + // handler ignored - tests should not induce conflicts + String pathname = dataType.getPathName(); + DataType myDt = dataTypeMap.get(pathname); + if (myDt != null) { + return myDt; + } + DataType dt = dataType.clone(this); + dataTypeMap.put(pathname, dt); + return dt; + } + + @Override + public DataType findDataType(String dataTypePath) { + return dataTypeMap.get(dataTypePath); + } + + @Override + public DataType getDataType(CategoryPath path, String name) { + return super.getDataType(new DataTypePath(path, name).getPath()); + } + + @Override + public DataType getDataType(String dataTypePath) { + return dataTypeMap.get(dataTypePath); + } + }; + + dataTypeParser = new PdbDataTypeParser(dataMgr, null, null); + } + + @Override + public void accept(String message) { + Msg.info(this, message + " (Test: " + getName() + ")"); + } + + @Test + public void testSimpleStructureWithBitFields() throws Exception { + + StructureDataType struct = new StructureDataType("struct", 0, dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList(new MyPdbMember("a", "uchar", 0), + new MyPdbMember("b:0x4", "uchar", 1), + new MyPdbMember("c:0x4", "uchar", 1), + new MyPdbMember("d:0x4", "uchar", 2), + new MyPdbMember("e:0x4", "uint", 4), + new MyPdbMember("f", "ushort", 8)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/struct\n" + + "Aligned\n" + + "Structure struct {\n" + + " 0 uchar 1 a \"\"\n" + + " 1 uchar:4(0) 1 b \"\"\n" + + " 1 uchar:4(4) 1 c \"\"\n" + + " 2 uchar:4(0) 1 d \"\"\n" + + " 4 uint:4(0) 1 e \"\"\n" + + " 8 ushort 2 f \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testSimpleStructureWithFillerBitFields() throws Exception { + + StructureDataType struct = new StructureDataType("struct", 0, dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList(new MyPdbMember("a", "uchar", 0), + new MyPdbMember("b:0x2:0x2", "uchar", 1), + new MyPdbMember("c1:0x1:0x4", "uchar", 1), + new MyPdbMember("c2:0x1:0x6", "uchar", 1), + new MyPdbMember("d:0x7:0x0", "uchar", 2), + new MyPdbMember("e:0x4:0x0", "uint", 4), + new MyPdbMember("f", "ushort", 8)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/struct\n" + + "Aligned\n" + + "Structure struct {\n" + + " 0 uchar 1 a \"\"\n" + + " 1 uchar:2(0) 1 padding \"\"\n" + + " 1 uchar:2(2) 1 b \"\"\n" + + " 1 uchar:1(4) 1 c1 \"\"\n" + + " 1 uchar:1(5) 1 padding \"\"\n" + + " 1 uchar:1(6) 1 c2 \"\"\n" + + " 2 uchar:7(0) 1 d \"\"\n" + + " 4 uint:4(0) 1 e \"\"\n" + + " 8 ushort 2 f \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct, true); + //@formatter:on + } + + @Test + public void testUnionedStructuresWithFillerBitFields() throws Exception { + + /*** Structure Source *** + + struct BitFieldUnionAlternatingPadded_s { + union { + struct { + char a0:1; + char :1; + char a2:1; + char :1; + char a4:1; + char :1; + }; + struct { + char :1; + char a1:1; + char :1; + char a3:1; + char :1; + char a5:1; + }; + }; + }; + + */ + + StructureDataType struct = new StructureDataType("struct", 0, dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList( + new MyPdbMember("a0:0x1:0x0", "char", 0), + new MyPdbMember("a2:0x1:0x2", "char", 0), + new MyPdbMember("a4:0x1:0x4", "char", 0), + new MyPdbMember("a1:0x1:0x1", "char", 0), + new MyPdbMember("a3:0x1:0x3", "char", 0), + new MyPdbMember("a5:0x1:0x5", "char", 0)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 1, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/struct\n" + + "Aligned\n" + + "Structure struct {\n" + + " 0 struct_u_0 1 null \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/struct/struct_u_0\n" + + "Aligned\n" + + "Union struct_u_0 {\n" + + " 0 struct_u_0_s_0 1 _s_0 \"\"\n" + + " 0 struct_u_0_s_1 1 _s_1 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/struct/struct_u_0/struct_u_0_s_0\n" + + "Aligned\n" + + "Structure struct_u_0_s_0 {\n" + + " 0 char:1(0) 1 a0 \"\"\n" + + " 0 char:1(1) 1 padding \"\"\n" + + " 0 char:1(2) 1 a2 \"\"\n" + + " 0 char:1(3) 1 padding \"\"\n" + + " 0 char:1(4) 1 a4 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/struct/struct_u_0/struct_u_0_s_1\n" + + "Aligned\n" + + "Structure struct_u_0_s_1 {\n" + + " 0 char:1(0) 1 padding \"\"\n" + + " 0 char:1(1) 1 a1 \"\"\n" + + " 0 char:1(2) 1 padding \"\"\n" + + " 0 char:1(3) 1 a3 \"\"\n" + + " 0 char:1(4) 1 padding \"\"\n" + + " 0 char:1(5) 1 a5 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct, true); + //@formatter:on + } + + @Test + public void testSimpleUnion() throws Exception { + + UnionDataType union = new UnionDataType(CategoryPath.ROOT, "union", dataMgr); + + // NOTE: preference will be given to packing compatible bitfields at same byte + // offset into structures (currently ambiguous data in PDB xml) + + //@formatter:off + List members = + CollectionUtils.asList(new MyPdbMember("a", "uchar", 0), + new MyPdbMember("b:0x4", "uchar", 0), + new MyPdbMember("c:0x4", "uchar", 0), + new MyPdbMember("d:0x4", "uchar", 0), + new MyPdbMember("e:0x4", "uchar", 0), + new MyPdbMember("f", "ushort", 0)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(union, false, 2, members, this, + TaskMonitor.DUMMY)); + + // NOTE: handling of bit-fields within union is rather ambiguous within + // PDB data + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/union\n" + + "Aligned\n" + + "Union union {\n" + + " 0 uchar 1 a \"\"\n" + + " 0 union_s_1 1 _s_1 \"\"\n" + + " 0 union_s_2 1 _s_2 \"\"\n" + + " 0 ushort 2 f \"\"\n" + + "}\n" + + "Size = 2 Actual Alignment = 2\n" + + "/union/union_s_1\n" + + "Aligned\n" + + "Structure union_s_1 {\n" + + " 0 uchar:4(0) 1 b \"\"\n" + + " 0 uchar:4(4) 1 c \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/union/union_s_2\n" + + "Aligned\n" + + "Structure union_s_2 {\n" + + " 0 uchar:4(0) 1 d \"\"\n" + + " 0 uchar:4(4) 1 e \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", union, true); + //@formatter:on + } + + @Test + public void testComplexStructureWithFlexArray() throws Exception { + + UnionDataType struct = new UnionDataType(CategoryPath.ROOT, "union", dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList( + new MyPdbMember("a", "int", 0), + new MyPdbMember("b", "int", 4), + new MyPdbMember("c", "int", 8), + new MyPdbMember("d", "char[0]", 12), + new MyPdbMember("e", "longlong", 0), + new MyPdbMember("f", "char[0]", 8)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/union\n" + + "Aligned\n" + + "Union union {\n" + + " 0 union_s_0 12 _s_0 \"\"\n" + + " 0 union_s_1 8 _s_1 \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 8\n" + + "/union/union_s_0\n" + + "Aligned\n" + + "Structure union_s_0 {\n" + + " 0 int 4 a \"\"\n" + + " 4 int 4 b \"\"\n" + + " 8 int 4 c \"\"\n" + + " char[0] 0 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4\n" + + "/union/union_s_1\n" + + "Aligned\n" + + "Structure union_s_1 {\n" + + " 0 longlong 8 e \"\"\n" + + " char[0] 0 f \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8\n", struct, true); + //@formatter:on + } + + @Test + public void testComplexStructureWithFlexArray2() throws Exception { + + UnionDataType struct = new UnionDataType(CategoryPath.ROOT, "union", dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList( + new MyPdbMember("a", "int", 0), + new MyPdbMember("b", "int", 4), + new MyPdbMember("c", "int", 8), + new MyPdbMember("d", "char[0]", 12), + new MyPdbMember("f", "char[0]", 0)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/union\n" + + "Aligned\n" + + "Union union {\n" + + " 0 union_s_0 12 _s_0 \"\"\n" + + " 0 union_s_1 1 _s_1 \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4\n" + + "/union/union_s_0\n" + + "Aligned\n" + + "Structure union_s_0 {\n" + + " 0 int 4 a \"\"\n" + + " 4 int 4 b \"\"\n" + + " 8 int 4 c \"\"\n" + + " char[0] 0 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4\n" + + "/union/union_s_1\n" + + "Aligned\n" + + "Structure union_s_1 {\n" + + " char[0] 0 f \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n", struct, true); + //@formatter:on + } + + @Test + public void testComplexStructureWithBitFields() throws Exception { + + StructureDataType struct = new StructureDataType("struct", 0, dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList( + new MyPdbMember("s0:0x1", "char", 0), + new MyPdbMember("s1:0x1", "char", 0), + new MyPdbMember("s2:0x1", "char", 0), + new MyPdbMember("s3:0x1", "char", 0), + new MyPdbMember("s4:0x1", "char", 0), + new MyPdbMember("s5:0x1", "char", 0), + new MyPdbMember("s6:0x1", "char", 0), + new MyPdbMember("s7:0x1", "char", 0), + new MyPdbMember("u0:0x1", "uchar", 0), + new MyPdbMember("u1:0x1", "uchar", 0), + new MyPdbMember("u2:0x1", "uchar", 0), + new MyPdbMember("u3:0x1", "uchar", 0), + new MyPdbMember("u4:0x1", "uchar", 0), + new MyPdbMember("u5:0x1", "uchar", 0), + new MyPdbMember("u6:0x1", "uchar", 0), + new MyPdbMember("u7:0x1", "uchar", 0), + new MyPdbMember("a", "ulong", 8), + new MyPdbMember("b", "longlong", 8)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 16, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/struct\n" + + "Aligned\n" + + "Structure struct {\n" + + " 0 struct_u_0 1 null \"\"\n" + + " 8 struct_u_8 8 null \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8\n" + + "/struct/struct_u_0\n" + + "Aligned\n" + + "Union struct_u_0 {\n" + + " 0 struct_u_0_s_0 1 _s_0 \"\"\n" + + " 0 struct_u_0_s_1 1 _s_1 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/struct/struct_u_0/struct_u_0_s_0\n" + + "Aligned\n" + + "Structure struct_u_0_s_0 {\n" + + " 0 char:1(0) 1 s0 \"\"\n" + + " 0 char:1(1) 1 s1 \"\"\n" + + " 0 char:1(2) 1 s2 \"\"\n" + + " 0 char:1(3) 1 s3 \"\"\n" + + " 0 char:1(4) 1 s4 \"\"\n" + + " 0 char:1(5) 1 s5 \"\"\n" + + " 0 char:1(6) 1 s6 \"\"\n" + + " 0 char:1(7) 1 s7 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/struct/struct_u_0/struct_u_0_s_1\n" + + "Aligned\n" + + "Structure struct_u_0_s_1 {\n" + + " 0 uchar:1(0) 1 u0 \"\"\n" + + " 0 uchar:1(1) 1 u1 \"\"\n" + + " 0 uchar:1(2) 1 u2 \"\"\n" + + " 0 uchar:1(3) 1 u3 \"\"\n" + + " 0 uchar:1(4) 1 u4 \"\"\n" + + " 0 uchar:1(5) 1 u5 \"\"\n" + + " 0 uchar:1(6) 1 u6 \"\"\n" + + " 0 uchar:1(7) 1 u7 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/struct/struct_u_8\n" + + "Aligned\n" + + "Union struct_u_8 {\n" + + " 0 ulong 4 a \"\"\n" + + " 0 longlong 8 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8\n", struct, true); + //@formatter:on + } + + @Test + public void testComplexStructureWithBitFields2() throws Exception { + + UnionDataType struct = new UnionDataType(CategoryPath.ROOT, "union", dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList( + new MyPdbMember("a", "ulong", 0), + new MyPdbMember("b", "longlong", 0), + new MyPdbMember("s0:0x1", "char", 0), + new MyPdbMember("s1:0x1", "char", 0), + new MyPdbMember("s2:0x1", "char", 0), + new MyPdbMember("s3:0x1", "char", 0), + new MyPdbMember("s4:0x1", "char", 0), + new MyPdbMember("s5:0x1", "char", 0), + new MyPdbMember("s6:0x1", "char", 0), + new MyPdbMember("s7:0x1", "char", 0), + new MyPdbMember("u0:0x1", "uchar", 1), + new MyPdbMember("u1:0x1", "uchar", 1), + new MyPdbMember("u2:0x1", "uchar", 1), + new MyPdbMember("u3:0x1", "uchar", 1), + new MyPdbMember("u4:0x1", "uchar", 1), + new MyPdbMember("u5:0x1", "uchar", 1), + new MyPdbMember("u6:0x1", "uchar", 1), + new MyPdbMember("u7:0x1", "uchar", 1)); + + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/union\n" + + "Unaligned\n" + + "Union union {\n" + + " 0 ulong 4 a \"\"\n" + + " 0 longlong 8 b \"\"\n" + + " 0 union_s_2 2 _s_2 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1\n" + + "/union/union_s_2\n" + + "Aligned\n" + + "Structure union_s_2 {\n" + + " 0 char:1(0) 1 s0 \"\"\n" + + " 0 char:1(1) 1 s1 \"\"\n" + + " 0 char:1(2) 1 s2 \"\"\n" + + " 0 char:1(3) 1 s3 \"\"\n" + + " 0 char:1(4) 1 s4 \"\"\n" + + " 0 char:1(5) 1 s5 \"\"\n" + + " 0 char:1(6) 1 s6 \"\"\n" + + " 0 char:1(7) 1 s7 \"\"\n" + + " 1 uchar:1(0) 1 u0 \"\"\n" + + " 1 uchar:1(1) 1 u1 \"\"\n" + + " 1 uchar:1(2) 1 u2 \"\"\n" + + " 1 uchar:1(3) 1 u3 \"\"\n" + + " 1 uchar:1(4) 1 u4 \"\"\n" + + " 1 uchar:1(5) 1 u5 \"\"\n" + + " 1 uchar:1(6) 1 u6 \"\"\n" + + " 1 uchar:1(7) 1 u7 \"\"\n" + + "}\n" + + "Size = 2 Actual Alignment = 1", struct, true); + //@formatter:on + } + + // @Ignore defined structure is too ambiguous + @Test + public void testMoreComplicatedStructure() throws Exception { + + StructureDataType struct = new StructureDataType("MoreComplicated_s", 0, dataMgr); + + //@formatter:off + List members = + CollectionUtils.asList(new MyPdbMember("s0:0x1", "char", 0x0), + new MyPdbMember("s1:0x1", "char", 0x0), + new MyPdbMember("s2:0x1", "char", 0x0), + new MyPdbMember("s3:0x1", "char", 0x0), + new MyPdbMember("s4:0x1", "char", 0x0), + new MyPdbMember("s5:0x1", "char", 0x0), + new MyPdbMember("s6:0x1", "char", 0x0), + new MyPdbMember("s7:0x1", "char", 0x0), + new MyPdbMember("u0:0x1", "uchar", 0x0), + new MyPdbMember("u1:0x1", "uchar", 0x0), + new MyPdbMember("u2:0x1", "uchar", 0x0), + new MyPdbMember("u3:0x1", "uchar", 0x0), + new MyPdbMember("u4:0x1", "uchar", 0x0), + new MyPdbMember("u5:0x1", "uchar", 0x0), + new MyPdbMember("u6:0x1", "uchar", 0x0), + new MyPdbMember("u7:0x1", "uchar", 0x0), + new MyPdbMember("val", "ulong", 0x8), + new MyPdbMember("d", "double", 0x8), + new MyPdbMember("n0:0x4", "ulong", 0x8), + new MyPdbMember("n1:0x4", "ulong", 0x8), + new MyPdbMember("n2:0x4", "ulong", 0x8), + new MyPdbMember("n3:0x4", "ulong", 0x8), + new MyPdbMember("n4:0x4", "ulong", 0x8), + new MyPdbMember("n5:0x4", "ulong", 0x8), + new MyPdbMember("n6:0x4", "ulong", 0x8), + new MyPdbMember("n7:0x4", "ulong", 0x8), + new MyPdbMember("n8:0x4", "ulong", 0xc), + new MyPdbMember("n9:0x4", "ulong", 0xc), + new MyPdbMember("n10:0x4", "ulong", 0xc), + new MyPdbMember("n11:0x4", "ulong", 0xc), + new MyPdbMember("n12:0x4", "ulong", 0xc), + new MyPdbMember("n13:0x4", "ulong", 0xc), + new MyPdbMember("n14:0x4", "ulong", 0xc), + new MyPdbMember("n15:0x4", "ulong", 0xc), + new MyPdbMember("x1:0x1", "ulong", 0x8), + new MyPdbMember("x2:0x2", "ulong", 0x8), + new MyPdbMember("x3:0x3", "ulong", 0x8), + new MyPdbMember("x4:0x4", "ulong", 0x8), + new MyPdbMember("x5:0x5", "ulong", 0x8), + new MyPdbMember("x6:0x6", "ulong", 0x8), + new MyPdbMember("x7:0x7", "ulong", 0x8), + new MyPdbMember("x8:0x8", "ulong", 0xc), + new MyPdbMember("x9:0x9", "ulong", 0xc), + new MyPdbMember("x10:0xa", "ulong", 0xc), + new MyPdbMember("y1:0x1", "uchar", 0x8), + new MyPdbMember("y2:0x2", "uchar", 0x8), + new MyPdbMember("y3:0x3", "uchar", 0x8), + new MyPdbMember("y4:0x4", "uchar", 0x9), + new MyPdbMember("y5:0x5", "uchar", 0xa), + new MyPdbMember("y6:0x6", "uchar", 0xb), + new MyPdbMember("y7:0x7", "uchar", 0xc), + new MyPdbMember("y8:0x8", "uchar", 0xd), + new MyPdbMember("y9:0x9", "ushort", 0xe), + new MyPdbMember("da", "double", 0x10), + new MyPdbMember("ca", "char[8]", 0x18), + new MyPdbMember("cb", "char[8]", 0x10), + new MyPdbMember("db", "double", 0x18), + new MyPdbMember("beef", "char[10]", 0x20), + new MyPdbMember("fromAddress", "int", 0x2c), + new MyPdbMember("toAddress", "int", 0x30), + new MyPdbMember("seqNum", "int", 0x34), + new MyPdbMember("data", "char[0]", 0x38), + new MyPdbMember("buf", "char[0]", 0x2c)); + //@formatter:on + + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 0x38, members, this, + TaskMonitor.DUMMY)); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, + "/MoreComplicated_s\n" + + "Aligned\n" + + "Structure MoreComplicated_s {\n" + + " 0 MoreComplicated_s_u_0 1 null \"\"\n" + + " 8 MoreComplicated_s_u_8 8 null \"\"\n" + + " 16 MoreComplicated_s_u_16 16 null \"\"\n" + + " 32 char[10] 10 beef \"\"\n" + + " 44 MoreComplicated_s_u_44 12 null \"\"\n" + + "}\n" + + "Size = 56 Actual Alignment = 8\n" + + "/MoreComplicated_s/MoreComplicated_s_u_0\n" + + "Aligned\n" + + "Union MoreComplicated_s_u_0 {\n" + + " 0 MoreComplicated_s_u_0_s_0 1 _s_0 \"\"\n" + + " 0 MoreComplicated_s_u_0_s_1 1 _s_1 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/MoreComplicated_s/MoreComplicated_s_u_0/MoreComplicated_s_u_0_s_0\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_0_s_0 {\n" + + " 0 char:1(0) 1 s0 \"\"\n" + + " 0 char:1(1) 1 s1 \"\"\n" + + " 0 char:1(2) 1 s2 \"\"\n" + + " 0 char:1(3) 1 s3 \"\"\n" + + " 0 char:1(4) 1 s4 \"\"\n" + + " 0 char:1(5) 1 s5 \"\"\n" + + " 0 char:1(6) 1 s6 \"\"\n" + + " 0 char:1(7) 1 s7 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/MoreComplicated_s/MoreComplicated_s_u_0/MoreComplicated_s_u_0_s_1\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_0_s_1 {\n" + + " 0 uchar:1(0) 1 u0 \"\"\n" + + " 0 uchar:1(1) 1 u1 \"\"\n" + + " 0 uchar:1(2) 1 u2 \"\"\n" + + " 0 uchar:1(3) 1 u3 \"\"\n" + + " 0 uchar:1(4) 1 u4 \"\"\n" + + " 0 uchar:1(5) 1 u5 \"\"\n" + + " 0 uchar:1(6) 1 u6 \"\"\n" + + " 0 uchar:1(7) 1 u7 \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/MoreComplicated_s/MoreComplicated_s_u_16\n" + + "Aligned\n" + + "Union MoreComplicated_s_u_16 {\n" + + " 0 MoreComplicated_s_u_16_s_0 16 _s_0 \"\"\n" + + " 0 MoreComplicated_s_u_16_s_1 16 _s_1 \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8\n" + + "/MoreComplicated_s/MoreComplicated_s_u_16/MoreComplicated_s_u_16_s_0\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_16_s_0 {\n" + + " 0 double 8 da \"\"\n" + + " 8 char[8] 8 ca \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8\n" + + "/MoreComplicated_s/MoreComplicated_s_u_16/MoreComplicated_s_u_16_s_1\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_16_s_1 {\n" + + " 0 char[8] 8 cb \"\"\n" + + " 8 double 8 db \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8\n" + + "/MoreComplicated_s/MoreComplicated_s_u_44\n" + + "Aligned\n" + + "Union MoreComplicated_s_u_44 {\n" + + " 0 MoreComplicated_s_u_44_s_0 12 _s_0 \"\"\n" + + " 0 MoreComplicated_s_u_44_s_1 1 _s_1 \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4\n" + + "/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_0\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_44_s_0 {\n" + + " 0 int 4 fromAddress \"\"\n" + + " 4 int 4 toAddress \"\"\n" + + " 8 int 4 seqNum \"\"\n" + + " char[0] 0 data \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4\n" + + "/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_1\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_44_s_1 {\n" + + " char[0] 0 buf \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1\n" + + "/MoreComplicated_s/MoreComplicated_s_u_8\n" + + "Aligned\n" + + "Union MoreComplicated_s_u_8 {\n" + + " 0 ulong 4 val \"\"\n" + + " 0 double 8 d \"\"\n" + + " 0 MoreComplicated_s_u_8_s_2 8 _s_2 \"\"\n" + + " 0 MoreComplicated_s_u_8_s_3 8 _s_3 \"\"\n" + + " 0 MoreComplicated_s_u_8_s_4 8 _s_4 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8\n" + + "/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_2\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_8_s_2 {\n" + + " 0 ulong:4(0) 1 n0 \"\"\n" + + " 0 ulong:4(4) 1 n1 \"\"\n" + + " 1 ulong:4(0) 1 n2 \"\"\n" + + " 1 ulong:4(4) 1 n3 \"\"\n" + + " 2 ulong:4(0) 1 n4 \"\"\n" + + " 2 ulong:4(4) 1 n5 \"\"\n" + + " 3 ulong:4(0) 1 n6 \"\"\n" + + " 3 ulong:4(4) 1 n7 \"\"\n" + + " 4 ulong:4(0) 1 n8 \"\"\n" + + " 4 ulong:4(4) 1 n9 \"\"\n" + + " 5 ulong:4(0) 1 n10 \"\"\n" + + " 5 ulong:4(4) 1 n11 \"\"\n" + + " 6 ulong:4(0) 1 n12 \"\"\n" + + " 6 ulong:4(4) 1 n13 \"\"\n" + + " 7 ulong:4(0) 1 n14 \"\"\n" + + " 7 ulong:4(4) 1 n15 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4\n" + + "/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_3\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_8_s_3 {\n" + + " 0 ulong:1(0) 1 x1 \"\"\n" + + " 0 ulong:2(1) 1 x2 \"\"\n" + + " 0 ulong:3(3) 1 x3 \"\"\n" + + " 0 ulong:4(6) 2 x4 \"\"\n" + + " 1 ulong:5(2) 1 x5 \"\"\n" + + " 1 ulong:6(7) 2 x6 \"\"\n" + + " 2 ulong:7(5) 2 x7 \"\"\n" + + " 4 ulong:8(0) 1 x8 \"\"\n" + + " 5 ulong:9(0) 2 x9 \"\"\n" + + " 6 ulong:10(1) 2 x10 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4\n" + + "/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_4\n" + + "Aligned\n" + + "Structure MoreComplicated_s_u_8_s_4 {\n" + + " 0 uchar:1(0) 1 y1 \"\"\n" + + " 0 uchar:2(1) 1 y2 \"\"\n" + + " 0 uchar:3(3) 1 y3 \"\"\n" + + " 1 uchar:4(0) 1 y4 \"\"\n" + + " 2 uchar:5(0) 1 y5 \"\"\n" + + " 3 uchar:6(0) 1 y6 \"\"\n" + + " 4 uchar:7(0) 1 y7 \"\"\n" + + " 5 uchar:8(0) 1 y8 \"\"\n" + + " 6 ushort:9(0) 2 y9 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct, true); + //@formatter:on + } + + class MyPdbMember extends DefaultPdbMember { + + protected MyPdbMember(String memberName, String memberDataTypeName, int memberOffset) { + super(memberName, memberDataTypeName, memberOffset, dataTypeParser); + } + + } + +} diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java index 58ce5afed7..66fa664234 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java @@ -23,7 +23,6 @@ import java.util.List; import org.junit.*; -import generic.test.AbstractGenericTest; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.bin.format.pdb.PdbParserNEW.PdbFileType; @@ -35,7 +34,8 @@ import ghidra.program.model.address.AddressFactory; import ghidra.program.model.listing.FunctionManager; import ghidra.program.model.listing.Program; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; +import utilities.util.FileUtilities; public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @@ -84,7 +84,7 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { public void setUp() throws Exception { // Get temp directory in which to store files - String tempDirPath = AbstractGenericTest.getTestDirectoryPath(); + String tempDirPath = getTestDirectoryPath(); tempDir = new File(tempDirPath); fileLocation = new File(tempDir, exeFolderName); @@ -100,6 +100,9 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @After public void tearDown() throws Exception { + if (fileLocation != null) { + FileUtilities.deleteDir(fileLocation); + } if (createdFiles != null) { deleteCreatedFiles(createdFiles); } @@ -991,9 +994,8 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { } private void createDirectory(File directory) { - boolean createSuccess = directory.mkdir(); - - if (!createSuccess) { + directory.mkdir(); + if (!directory.isDirectory()) { fail("Should have created directory: " + directory.getAbsolutePath()); } } @@ -1063,11 +1065,12 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(testProgram); DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService(); - PdbParserNEW parser = new PdbParserNEW(pdb, testProgram, dataTypeManagerService, false); + PdbParserNEW parser = + new PdbParserNEW(pdb, testProgram, dataTypeManagerService, false, TaskMonitor.DUMMY); parser.openDataTypeArchives(); parser.parse(); - parser.applyTo(TaskMonitorAdapter.DUMMY_MONITOR, new MessageLog()); + parser.applyTo(new MessageLog()); // Now check program to see if the function has been successfully applied AddressFactory addressFactory = testProgram.getAddressFactory(); diff --git a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractFunctionParameterMarkupItemTest.java b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractFunctionParameterMarkupItemTest.java index e0b3fe479b..05a09510be 100644 --- a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractFunctionParameterMarkupItemTest.java +++ b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractFunctionParameterMarkupItemTest.java @@ -34,7 +34,7 @@ public abstract class AbstractFunctionParameterMarkupItemTest extends AbstractVT //================================================================================================== protected DataType createByteDataType(ProgramDB program) { - DataTypeManagerDB dataTypeManager = program.getDataManager(); + DataTypeManagerDB dataTypeManager = program.getDataTypeManager(); DataType dataType = new ByteDataType(dataTypeManager); return dataTypeManager.addDataType(dataType, DataTypeConflictHandler.DEFAULT_HANDLER); } @@ -43,7 +43,7 @@ public abstract class AbstractFunctionParameterMarkupItemTest extends AbstractVT int transaction = -1; try { transaction = program.startTransaction("Test - Create Data Type"); - DataTypeManagerDB dataTypeManager = program.getDataManager(); + DataTypeManagerDB dataTypeManager = program.getDataTypeManager(); DataType dataType = new IntegerDataType(dataTypeManager); return dataTypeManager.addDataType(dataType, DataTypeConflictHandler.DEFAULT_HANDLER); } diff --git a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java index 00aac4fa7c..dcf8c97d73 100644 --- a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java +++ b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java @@ -390,7 +390,7 @@ public class DBHandle { /** * Returns true if transaction is currently active */ - protected boolean isTransactionActive() { + public boolean isTransactionActive() { return txStarted; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/layout/TwoColumnPairLayout.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/layout/TwoColumnPairLayout.java index a7b054824a..b263e44be0 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/layout/TwoColumnPairLayout.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/layout/TwoColumnPairLayout.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,7 @@ */ package ghidra.util.layout; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Insets; -import java.awt.LayoutManager; +import java.awt.*; /** * LayoutManger for arranging components into exactly two columns. @@ -30,14 +25,16 @@ public class TwoColumnPairLayout implements LayoutManager { private int columnGap; private int pairGap; private int preferredColumnWidth; + /** * Constructor for PairLayout. */ public TwoColumnPairLayout() { - this(0,0,0,0); + this(0, 0, 0, 0); } - - public TwoColumnPairLayout(int verticalGap, int columnGap, int pairGap, int preferredValueColumnWidth) { + + public TwoColumnPairLayout(int verticalGap, int columnGap, int pairGap, + int preferredValueColumnWidth) { super(); this.verticalGap = verticalGap; this.columnGap = columnGap; @@ -45,39 +42,46 @@ public class TwoColumnPairLayout implements LayoutManager { this.preferredColumnWidth = preferredValueColumnWidth; } - - /** * @see LayoutManager#addLayoutComponent(String, Component) */ - public void addLayoutComponent(String name, Component comp) {} + @Override + public void addLayoutComponent(String name, Component comp) { + } /** * @see LayoutManager#removeLayoutComponent(Component) */ - public void removeLayoutComponent(Component comp) {} + @Override + public void removeLayoutComponent(Component comp) { + } /** * @see LayoutManager#preferredLayoutSize(Container) */ + @Override public Dimension preferredLayoutSize(Container parent) { int rowHeight = getPreferredRowHeight(parent); int[] widths = getPreferredWidths(parent); - + int nRows = (parent.getComponentCount() + 3) / 4; Insets insets = parent.getInsets(); - Dimension d = new Dimension(0,0); - int labelWidth = widths[0]; - int valueWidth = preferredColumnWidth == 0 ? widths[1] : preferredColumnWidth; - - d.width = 2*labelWidth + 2*valueWidth + columnGap + 2*pairGap + insets.left + insets.right; - d.height = rowHeight *nRows + verticalGap * (nRows-1) + insets.top + insets.bottom; + Dimension d = new Dimension(0, 0); + + if (preferredColumnWidth > 0) { + widths[1] = widths[3] = preferredColumnWidth; + } + + d.width = widths[0] + widths[1] + widths[2] + widths[3] + columnGap + 2 * pairGap + + insets.left + insets.right; + d.height = rowHeight * nRows + verticalGap * (nRows - 1) + insets.top + insets.bottom; return d; - } + } /** * @see LayoutManager#minimumLayoutSize(Container) */ + @Override public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } @@ -85,6 +89,7 @@ public class TwoColumnPairLayout implements LayoutManager { /** * @see LayoutManager#layoutContainer(Container) */ + @Override public void layoutContainer(Container parent) { int rowHeight = getPreferredRowHeight(parent); int[] widths = getPreferredWidths(parent); @@ -94,16 +99,21 @@ public class TwoColumnPairLayout implements LayoutManager { int width = d.width - (insets.left + insets.right); int x = insets.left; int y = insets.top; - - widths[1] = (width - 2*widths[0] - 2*pairGap - columnGap)/2; + + int totalLabelWidth = widths[0] + widths[2]; + int padding = 2 * pairGap + columnGap; + int totalValueWidth = (width - totalLabelWidth - padding); + + widths[1] = (totalValueWidth * widths[1]) / (widths[1] + widths[3]); + widths[3] = totalValueWidth - widths[1]; int n = parent.getComponentCount(); - for(int i=0;i> circuits = GraphAlgorithms.findCircuits(g, TaskMonitor.DUMMY); + List> circuits = GraphAlgorithms.findCircuits(g, false, TaskMonitor.DUMMY); assertEquals(3, circuits.size()); } diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg index 2e9f22b3ac..1f9384ef2f 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg @@ -71,7 +71,6 @@ - @@ -82,6 +81,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java index bbfd9174a4..48a50c9704 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java @@ -15,7 +15,7 @@ */ package ghidra.app.util; -import java.util.ConcurrentModificationException; +import java.util.*; import ghidra.docking.settings.Settings; import ghidra.program.database.ProgramDB; @@ -53,7 +53,7 @@ public class PseudoData extends PseudoCodeUnit implements Data { this.dataType = dataType; baseDataType = getBaseDataType(dataType); if (program instanceof ProgramDB) { - dataMgr = ((ProgramDB) program).getDataManager(); + dataMgr = ((ProgramDB) program).getDataTypeManager(); } } @@ -103,7 +103,7 @@ public class PseudoData extends PseudoCodeUnit implements Data { } - /** + /** * @see ghidra.program.model.listing.Data#getComponent(int) */ @Override @@ -187,7 +187,7 @@ public class PseudoData extends PseudoCodeUnit implements Data { } /** - * + * * @see ghidra.program.model.listing.CodeUnit#toString() */ @Override @@ -384,6 +384,46 @@ public class PseudoData extends PseudoCodeUnit implements Data { return null; } + @Override + public List getComponentsContaining(int offset) { + List list = new ArrayList<>(); + if (offset < 0 || offset >= length) { + return null; + } + + if (baseDataType instanceof Array) { + Array array = (Array) baseDataType; + int elementLength = array.getElementLength(); + int index = offset / elementLength; + list.add(getComponent(index)); + } + else if (baseDataType instanceof Structure) { + Structure struct = (Structure) baseDataType; + DataTypeComponent dtc = struct.getComponentAt(offset); + // Logic handles overlapping bit-fields + while (dtc != null && offset <= (dtc.getOffset() + dtc.getLength() - 1)) { + int ordinal = dtc.getOrdinal(); + list.add(getComponent(ordinal++)); + dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null; + } + } + else if (baseDataType instanceof DynamicDataType) { + DynamicDataType ddt = (DynamicDataType) baseDataType; + DataTypeComponent dtc = ddt.getComponentAt(offset, this); + if (dtc != null) { + list.add(getComponent(dtc.getOrdinal())); + } + } + else if (baseDataType instanceof Union) { + if (offset == 0) { + for (int i = 0; i < getNumComponents(); i++) { + list.add(getComponent(i)); + } + } + } + return list; + } + /** * @see ghidra.program.model.listing.Data#getComponentIndex() */ @@ -431,14 +471,14 @@ public class PseudoData extends PseudoCodeUnit implements Data { // for(int i=0;i[] COL_CLASS = new Class[] { StringField.class }; private final static String[] COL_TYPES = new String[] { "Value" }; - private final static Schema SCHEMA = new Schema(0, StringField.class, "Key", COL_CLASS, - COL_TYPES); + private final static Schema SCHEMA = + new Schema(0, StringField.class, "Key", COL_CLASS, COL_TYPES); private ProjectDataTypeManager dataTypeManager; @@ -98,8 +96,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType * @throws InvalidNameException * @throws DuplicateNameException */ - public DataTypeArchiveDB(DomainFolder folder, String name, Object consumer) throws IOException, - DuplicateNameException, InvalidNameException { + public DataTypeArchiveDB(DomainFolder folder, String name, Object consumer) + throws IOException, DuplicateNameException, InvalidNameException { super(new DBHandle(), name, 500, 1000, consumer); this.name = name; @@ -118,7 +116,9 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType endTransaction(id, true); clearUndo(false); - folder.createFile(name, this, TaskMonitorAdapter.DUMMY_MONITOR); + if (folder != null) { + folder.createFile(name, this, TaskMonitorAdapter.DUMMY_MONITOR); + } success = true; } @@ -136,7 +136,7 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType } /** - * Constructs a new DataTypeArchivemDB + * Constructs a new DataTypeArchiveDB * @param dbh a handle to an open data type archive database. * @param openMode one of: * READ_ONLY: the original database will not be modified @@ -207,8 +207,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType private void propertiesRestore() { Options pl = getOptions(ARCHIVE_INFO); boolean origChangeState = changed; - pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "4.3", - null, "Version of Ghidra used to create this program."); + pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "4.3", null, + "Version of Ghidra used to create this program."); pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created"); // registerDefaultPointerSize(); changed = origChangeState; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java index 71ffcc1842..bd22f4d660 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java @@ -156,14 +156,15 @@ abstract public class DatabaseObject { * This method provides a cheap (lock free) way to test if an object is valid. If * this object is invalid, then the lock will be used to refresh as needed. * @param lock the lock that will be used if the object needs to be refreshed. + * @return true if object is valid, else false */ - public void validate(Lock lock) { + public boolean validate(Lock lock) { if (!deleted && !isInvalid()) { - return; + return true; } lock.acquire(); try { - checkIsValid(); + return checkIsValid(); } finally { lock.release(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ListingDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ListingDB.java index faeaccdbf9..6f0d329972 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ListingDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ListingDB.java @@ -595,7 +595,7 @@ class ListingDB implements Listing { */ @Override public DataTypeManager getDataTypeManager() { - return program.getDataManager(); + return program.getDataTypeManager(); } /* (non-Javadoc) diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java index 45e3f96f6f..992e326e2e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java @@ -30,7 +30,6 @@ import ghidra.framework.store.LockException; import ghidra.program.database.bookmark.BookmarkDBManager; import ghidra.program.database.code.CodeManager; import ghidra.program.database.code.InstructionDB; -import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.database.external.ExternalManagerDB; import ghidra.program.database.function.FunctionManagerDB; @@ -623,13 +622,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM return (TreeManager) managers[TREE_MGR]; } - /** - * Returns the DataManager - */ - public DataTypeManagerDB getDataManager() { - return (DataTypeManagerDB) managers[DATA_MGR]; - } - /** * @see ghidra.program.model.listing.Program#getDataTypeManager() */ @@ -2657,7 +2649,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM metadata.put("# of Defined Data", "" + listing.getNumDefinedData()); metadata.put("# of Functions", "" + getFunctionManager().getFunctionCount()); metadata.put("# of Symbols", "" + getSymbolTable().getNumSymbols()); - metadata.put("# of Data Types", "" + getDataManager().getDataTypeCount(true)); + metadata.put("# of Data Types", "" + getDataTypeManager().getDataTypeCount(true)); metadata.put("# of Data Type Categories", "" + getDataTypeManager().getCategoryCount()); Options propList = getOptions(Program.PROGRAM_INFO); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java index ae9a526158..29614610a2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java @@ -227,7 +227,7 @@ public class CodeManager implements ErrorHandler, ManagerDB { contextMgr = program.getProgramContext(); refManager = program.getReferenceManager(); propertyMapMgr = program.getUsrPropertyManager(); - dataManager = program.getDataManager(); + dataManager = program.getDataTypeManager(); protoMgr.setProgram(program); } @@ -1976,6 +1976,10 @@ public class CodeManager implements ErrorHandler, ManagerDB { DataDB data = null; try { + if (dataType instanceof BitFieldDataType) { + throw new CodeUnitInsertionException("Bitfields not supported for Data"); + } + DataType originalDataType = dataType; if (dataType instanceof FactoryDataType) { MemBuffer memBuffer = new MemoryBufferImpl(program.getMemory(), addr); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java index 668ff9b082..a608123d2f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java @@ -16,6 +16,7 @@ package ghidra.program.database.code; import java.util.ArrayList; +import java.util.List; import db.Record; import ghidra.docking.settings.Settings; @@ -66,7 +67,7 @@ class DataDB extends CodeUnitDB implements Data { dataType = DataType.DEFAULT; } this.dataType = dataType; - dataMgr = program.getDataManager(); + dataMgr = program.getDataTypeManager(); baseDataType = getBaseDataType(dataType); @@ -554,9 +555,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.Data#getComponentAt(int) - */ @Override public Data getComponentAt(int offset) { lock.acquire(); @@ -594,6 +592,56 @@ class DataDB extends CodeUnitDB implements Data { } + @Override + public List getComponentsContaining(int offset) { + List list = new ArrayList<>(); + lock.acquire(); + try { + checkIsValid(); + if (offset < 0 || offset >= length) { + return null; + } + + if (baseDataType instanceof Array) { + Array array = (Array) baseDataType; + int elementLength = array.getElementLength(); + int index = offset / elementLength; + list.add(getComponent(index)); + } + else if (baseDataType instanceof Structure) { + Structure struct = (Structure) baseDataType; + DataTypeComponent dtc = struct.getComponentAt(offset); + // Logic handles overlapping bit-fields + // Include if offset is contains within bounds of component + while (dtc != null && (offset >= dtc.getOffset()) && + (offset <= (dtc.getOffset() + dtc.getLength() - 1))) { + int ordinal = dtc.getOrdinal(); + list.add(getComponent(ordinal++)); + dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null; + } + } + else if (baseDataType instanceof DynamicDataType) { + DynamicDataType ddt = (DynamicDataType) baseDataType; + DataTypeComponent dtc = ddt.getComponentAt(offset, this); + if (dtc != null) { + list.add(getComponent(dtc.getOrdinal())); + } + } + else if (baseDataType instanceof Union) { + if (offset == 0) { + for (int i = 0; i < getNumComponents(); i++) { + list.add(getComponent(i)); + } + } + } + return list; + } + finally { + lock.release(); + } + + } + /** * @see ghidra.program.model.listing.Data#getComponentIndex() */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java new file mode 100644 index 0000000000..4da1383037 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BitFieldDBDataType.java @@ -0,0 +1,251 @@ +/* ### + * 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.program.database.data; + +import ghidra.program.model.data.*; +import ghidra.program.model.data.Enum; +import ghidra.util.Msg; +import ghidra.util.exception.AssertException; + +/** + * BitFieldDBDataType extends BitFieldDataType for DataTypeManagerDB use. + * This class provides the ability to generate a datatype ID and reconstruct a bit-field + * datatype from an ID. + */ +class BitFieldDBDataType extends BitFieldDataType { + + // Bit Field ID Encoding (expressed as the following 4-bit nibble fields): XXTTTTTTTTBBOOSS + // + // XX - reserved for datatype manager table ID + // TTTTTTTT - TypeDef/Enum ID (32-bits, excludes table ID, applies to resolved TypeDef/Enum only) + // BB - Encoded base type (8-bits, consists of the following bit fields: xttsbbbb) + // x - 1-bit, unused + // t - 2-bit, =0: base type only, =1:TypeDef used, =2: enum used, =3: abstract-int + // s - 1-bit, storage +1 + // xxxx - 4-bits, unused + // OO - bit offset (i.e., right-shift factor, relative to packing base type) + // SS - bit field size in bits + + private static final int BIT_OFFSET_SHIFT = 8; + private static final int BASE_TYPE_SHIFT = 16; + private static final int DATATYPE_INDEX_SHIFT = 24; + + public static final long MAX_DATATYPE_INDEX = 0xffffffffL; // 32-bits + + private static final long ID_TO_INDEX_MASK = ~-(1L << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT); + + BitFieldDBDataType(DataType baseDataType, int bitSize, int bitOffset, int storageSize, + DataTypeManager dtm) throws InvalidDataTypeException { + // avoid clone of baseDataType during construction + super(baseDataType, bitSize, bitOffset, storageSize); + } + + private static enum BaseDatatypeKind { + NONE(0), TYPEDEF(1), ENUM(2), INTEGER(3); + final int id; + + BaseDatatypeKind(int id) { + this.id = id; + } + + static BaseDatatypeKind getKind(int value) { + for (BaseDatatypeKind kind : values()) { + if (kind.id == value) { + return kind; + } + } + return NONE; + } + } + + /** + * Get a generated ID for this bit-field which is suitable for reconstruction + * via the {@link #getBitFieldDataType(long)} method. This ID encodes the base + * datatype (including typedef/enum and packing data), + * bit-size and bit-offset. The upper byte of the ID will always be zero and + * is reserved for use by the DataTypeManager. + *

+ * The ability to reference base datatypes (e.g., TypeDef, Enum) is currently limited + * (i.e. 32-bit base datatype ID). + * @param bitfieldDt the resolved bitfield datatype whose ID is needed. This must first be + * resolved by a DataTypeManagerDB. + * @return bit-field ID + */ + static final long getId(BitFieldDataType bitfieldDt) { + DataTypeManager dtm = bitfieldDt.getDataTypeManager(); + if (!(dtm instanceof DataTypeManagerDB)) { + throw new AssertException("bitfieldDt must first be resolved"); + } + + BaseDatatypeKind dataTypeKind = BaseDatatypeKind.NONE; + long dataTypeIndex = 0; + DataType baseDataType = bitfieldDt.getBaseDataType(); + if (baseDataType instanceof TypeDef) { + dataTypeKind = BaseDatatypeKind.TYPEDEF; + } + else if (baseDataType instanceof Enum) { + dataTypeKind = BaseDatatypeKind.ENUM; + } + else if (baseDataType instanceof AbstractIntegerDataType) { + dataTypeKind = BaseDatatypeKind.INTEGER; + } + if (dataTypeKind != BaseDatatypeKind.NONE) { + dataTypeIndex = getResolvedDataTypeIndex(baseDataType, (DataTypeManagerDB) dtm); + if (dataTypeIndex == DataTypeManager.NULL_DATATYPE_ID) { + Msg.debug(BitFieldDBDataType.class, + "Bit-Field data type not resolved: " + baseDataType.getName()); + dataTypeIndex = MAX_DATATYPE_INDEX; + dataTypeKind = BaseDatatypeKind.NONE; + } + else if (dataTypeIndex >= MAX_DATATYPE_INDEX) { + // TypeDef index exceeds 32-bit limit + Msg.debug(BitFieldDBDataType.class, + "Bit-Field data type index out of range: " + baseDataType.getName()); + dataTypeIndex = MAX_DATATYPE_INDEX; + dataTypeKind = BaseDatatypeKind.NONE; + } + } + long id = (dataTypeIndex << DATATYPE_INDEX_SHIFT) | + (getBaseTypeEncodedField(bitfieldDt, dataTypeKind) << BASE_TYPE_SHIFT) | + (bitfieldDt.getBitOffset() << BIT_OFFSET_SHIFT) | bitfieldDt.getDeclaredBitSize(); + return id; + } + + private static final long getBaseTypeEncodedField(BitFieldDataType bitFieldDt, + BaseDatatypeKind dataTypeKind) { + int nominalStorageSize = BitFieldDataType.getMinimumStorageSize(bitFieldDt.getBitSize()); + boolean extraStorageUsed = bitFieldDt.getStorageSize() > nominalStorageSize; + return (dataTypeKind.id << 5) | (extraStorageUsed ? 0x10L : 0L); + } + + /** + * Get a bit-field datatype instance for a given ID. The upper byte of the ID will be ignored. + * @param id bit-field datatype ID + * @param dtm data type manager + * @return bit-field data type + */ + static final BitFieldDataType getBitFieldDataType(long id, DataTypeManager dtm) { + + int bitSize = (int) (id & 0xff); // 8-bits + int bitOffset = (int) ((id >> BIT_OFFSET_SHIFT) & 0xff); // 8-bits + int baseTypeInfo = (int) ((id >> BASE_TYPE_SHIFT) & 0xff); // 8-bit encoded field + + BaseDatatypeKind baseDataTypeKind = BaseDatatypeKind.getKind((baseTypeInfo >> 5) & 3); + boolean extraStorageUsed = (baseTypeInfo & 0x10) != 0; + + DataType baseDataType = null; + long dataTypeIndex = (id >> DATATYPE_INDEX_SHIFT) & MAX_DATATYPE_INDEX; // 32-bits + if (baseDataTypeKind != BaseDatatypeKind.NONE && dataTypeIndex != MAX_DATATYPE_INDEX) { + if (baseDataTypeKind == BaseDatatypeKind.TYPEDEF) { + baseDataType = getTypeDef(dataTypeIndex, dtm); + } + else if (baseDataTypeKind == BaseDatatypeKind.ENUM) { + baseDataType = getEnum(dataTypeIndex, dtm); + } + else { + baseDataType = getIntegerType(dataTypeIndex, dtm); + } + } + try { + if (baseDataType == null) { + // use integer datatype on failure + baseDataType = IntegerDataType.dataType.clone(dtm); + } + int effectiveBitSize = getEffectiveBitSize(bitSize, baseDataType.getLength()); + int storageSize = getMinimumStorageSize(effectiveBitSize); + if (extraStorageUsed) { + ++storageSize; + } + return new BitFieldDBDataType(baseDataType, bitSize, bitOffset, storageSize, dtm); + } + catch (InvalidDataTypeException e) { + return null; + } + } + + private static final long getResolvedDataTypeIndex(DataType dataType, DataTypeManagerDB dtm) { + + long dataTypeId = dtm.getID(dataType); + if (dataTypeId == DataTypeManager.NULL_DATATYPE_ID) { + return DataTypeManager.NULL_DATATYPE_ID; + } + return dataTypeId & ID_TO_INDEX_MASK; + } + + /** + * Get the TypeDef which corresponds to the specified typeDefIndex and the + * specified data type manager. + * @param typeDefIndex base data type index used by bit-field + * @param primitiveBaseDataType expected primitive base datatype + * @param dtm data type manager + * @return TypeDef data type or null if not found + */ + private static final TypeDef getTypeDef(long typeDefIndex, DataTypeManager dtm) { + long dataTypeId = + ((long) DataTypeManagerDB.TYPEDEF << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT) | + typeDefIndex; + DataType dataType = dtm.getDataType(dataTypeId); + if (!(dataType instanceof TypeDef)) { + return null; + } + TypeDef typeDefDt = (TypeDef) dataType; + DataType dt = typeDefDt.getBaseDataType(); + if (dt instanceof Enum) { + // TODO: how restrictive should we be on matching enum size? + return typeDefDt; + } + if (dt instanceof AbstractIntegerDataType) { + return typeDefDt; + } + return null; // unsupported typedef + } + + /** + * Get the Enum which corresponds to the specified enumIndex and the + * specified data type manager. + * @param enumIndex enum data type index used by bit-field + * @param dtm data type manager + * @return Enum data type or null if not found + */ + private static final Enum getEnum(long enumIndex, DataTypeManager dtm) { + long dataTypeId = + ((long) DataTypeManagerDB.ENUM << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT) | enumIndex; + DataType dataType = dtm.getDataType(dataTypeId); + if (!(dataType instanceof Enum)) { + return null; + } + return (Enum) dataType; + } + + /** + * Get the integer base type which corresponds to the specified intTypeIndex and the + * specified data type manager. + * @param intTypeIndex base data type index used by bit-field + * @param dtm data type manager + * @return integer data type or null if not found + */ + private static final AbstractIntegerDataType getIntegerType(long intTypeIndex, + DataTypeManager dtm) { + long dataTypeId = + ((long) DataTypeManagerDB.BUILT_IN << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT) | + intTypeIndex; + DataType dataType = dtm.getDataType(dataTypeId); + if (!(dataType instanceof AbstractIntegerDataType)) { + return null; + } + return (AbstractIntegerDataType) dataType; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java index 7c64be5a3a..25254d6d3e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java @@ -24,7 +24,6 @@ import ghidra.program.model.data.*; import ghidra.program.model.mem.MemBuffer; import ghidra.util.UniversalID; import ghidra.util.exception.AssertException; -import ghidra.util.exception.InvalidInputException; /** * Database implementation for a structure or union. @@ -67,6 +66,34 @@ abstract class CompositeDB extends DataTypeDB implements Composite { */ protected abstract void initialize(); + /** + * Get the preferred length for a new component. For Unions and internally aligned + * structures the preferred component length for a fixed-length dataType will be the + * length of that dataType. Otherwise the length returned will be no larger then the + * specified length. + * @param dataType new component datatype + * @param length constrained length or -1 to force use of dataType size. Dynamic types + * such as string must have a positive length specified. + * @return preferred component length + */ + protected int getPreferredComponentLength(DataType dataType, int length) { + if ((isInternallyAligned() || (this instanceof Union)) && !(dataType instanceof Dynamic)) { + length = -1; // force use of datatype size + } + int dtLength = dataType.getLength(); + if (length <= 0) { + length = dtLength; + } + else if (dtLength > 0 && dtLength < length) { + length = dtLength; + } + if (length <= 0) { + throw new IllegalArgumentException("Positive length must be specified for " + + dataType.getDisplayName() + " component"); + } + return length; + } + @Override protected String doGetName() { return record.getString(CompositeDBAdapter.COMPOSITE_NAME_COL); @@ -78,20 +105,53 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } @Override - public DataTypeComponent add(DataType dataType) { - lock.acquire(); - try { - checkDeleted(); - int length = dataType.getLength(); - if (dataType.getLength() < 1) { - throw new IllegalArgumentException("Minimum data type length is 1 byte"); + public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName, + String comment) throws InvalidDataTypeException { + BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0, 0, dataMgr); + return add(bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); + } + + /** + * Handle replacement of datatype which may impact bitfield datatype. + * @param bitfieldComponent bitfield component + * @param oldDt affected datatype which has been removed or replaced + * @param newDt replacement datatype + * @param true if bitfield component was modified + * @throws InvalidDataTypeException if bitfield was based upon oldDt but new datatype is + * invalid for a bitfield + */ + protected boolean updateBitFieldDataType(DataTypeComponentDB bitfieldComponent, DataType oldDt, + DataType newDt) throws InvalidDataTypeException { + if (!bitfieldComponent.isBitFieldComponent()) { + throw new AssertException("expected bitfield component"); + } + + BitFieldDBDataType bitfieldDt = (BitFieldDBDataType) bitfieldComponent.getDataType(); + if (bitfieldDt.getBaseDataType() != oldDt) { + return false; + } + + if (newDt != null) { + BitFieldDataType.checkBaseDataType(newDt); + int maxBitSize = 8 * newDt.getLength(); + if (bitfieldDt.getBitSize() > maxBitSize) { + throw new InvalidDataTypeException("Replacement datatype too small for bitfield"); } - DataTypeComponent addedComponent = add(dataType, length, null, null); - return addedComponent; } - finally { - lock.release(); + + try { + BitFieldDBDataType newBitfieldDt = + new BitFieldDBDataType(newDt, bitfieldDt.getDeclaredBitSize(), + bitfieldDt.getBitOffset(), bitfieldDt.getStorageSize(), dataMgr); + bitfieldComponent.setDataType(newBitfieldDt); + oldDt.removeParent(this); + newDt.addParent(this); } + catch (InvalidDataTypeException e) { + throw new AssertException("unexpected"); + } + + return true; } @Override @@ -141,9 +201,6 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } } - /** - * @see ghidra.program.model.data.DataType#isDynamicallySized() - */ @Override public boolean isDynamicallySized() { return isInternallyAligned(); @@ -155,23 +212,28 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } @Override - public DataTypeComponent add(DataType dataType, int length) { + public final DataTypeComponent add(DataType dataType) { + return add(dataType, -1, null, null); + } + + @Override + public final DataTypeComponent add(DataType dataType, int length) { return add(dataType, length, null, null); } @Override - public DataTypeComponent add(DataType dataType, String fieldName, String comment) { - return add(dataType, dataType.getLength(), fieldName, comment); + public final DataTypeComponent add(DataType dataType, String fieldName, String comment) { + return add(dataType, -1, fieldName, comment); } @Override - public DataTypeComponent insert(int ordinal, DataType dataType, int length) { + public final DataTypeComponent insert(int ordinal, DataType dataType, int length) { return insert(ordinal, dataType, length, null, null); } @Override - public DataTypeComponent insert(int ordinal, DataType dataType) { - return insert(ordinal, dataType, dataType.getLength(), null, null); + public final DataTypeComponent insert(int ordinal, DataType dataType) { + return insert(ordinal, dataType, -1, null, null); } @Override @@ -179,13 +241,6 @@ abstract class CompositeDB extends DataTypeDB implements Composite { return getDisplayName(); } - /** - * Notifies the composite data type that a component in it has changed. - * @param component the component that changed. - */ - protected void componentChanged(DataTypeComponent component) { - } - @Override protected void doSetCategoryPathRecord(long categoryID) throws IOException { record.setLongValue(CompositeDBAdapter.COMPOSITE_CAT_COL, categoryID); @@ -236,6 +291,10 @@ abstract class CompositeDB extends DataTypeDB implements Composite { * @throws IllegalArgumentException if the data type is invalid. */ protected void validateDataType(DataType dataType) { + if (isInternallyAligned() && dataType == DataType.DEFAULT) { + throw new IllegalArgumentException( + "The DEFAULT data type is not allowed in an aligned composite data type."); + } if (dataType instanceof FactoryDataType) { throw new IllegalArgumentException("The \"" + dataType.getName() + "\" data type is not allowed in a composite data type."); @@ -350,7 +409,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } @Override - public void setPackingValue(int packingValue) throws InvalidInputException { + public void setPackingValue(int packingValue) { boolean changed = false; if (!isInternallyAligned()) { doSetInternallyAligned(true); @@ -366,9 +425,9 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } } - public void doSetPackingValue(int packingValue) throws InvalidInputException { + public void doSetPackingValue(int packingValue) { if (packingValue < 0) { - throw new InvalidInputException(packingValue + "is not a valid packing value."); + packingValue = NOT_PACKING; } lock.acquire(); try { @@ -432,7 +491,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } @Override - public void setMinimumAlignment(int externalAlignment) throws InvalidInputException { + public void setMinimumAlignment(int externalAlignment) { boolean changed = false; if (!isInternallyAligned()) { doSetInternallyAligned(true); @@ -447,10 +506,9 @@ abstract class CompositeDB extends DataTypeDB implements Composite { } } - public boolean doSetMinimumAlignment(int externalAlignment) throws InvalidInputException { - if (externalAlignment <= 0) { - throw new InvalidInputException(externalAlignment + - " is not a valid external alignment. It must be greater than 0."); + public boolean doSetMinimumAlignment(int externalAlignment) { + if (externalAlignment < 1) { + externalAlignment = DEFAULT_ALIGNED; } return modifyAlignment(externalAlignment); } @@ -504,6 +562,11 @@ abstract class CompositeDB extends DataTypeDB implements Composite { return false; } } + if (isDefaultAligned()) { + if (dbExternalAlignment == DEFAULT_ALIGNED) { + return false; + } + } else if (dbExternalAlignment == getMinimumAlignment()) { return false; } @@ -525,31 +588,17 @@ abstract class CompositeDB extends DataTypeDB implements Composite { * Notification that this composite data type's alignment has changed. */ protected void notifyAlignmentChanged() { - DataType[] dts = dataMgr.getParentDataTypes(key); - for (int i = 0; i < dts.length; i++) { - if (dts[i] instanceof Composite) { - Composite composite = (Composite) dts[i]; + // TODO: This method is not properly invoked when new components are + // added which could change the alignment of this composite + for (DataType dt : dataMgr.getParentDataTypes(key)) { + if (dt instanceof Composite) { + Composite composite = (Composite) dt; composite.dataTypeAlignmentChanged(this); } } dataMgr.dataTypeChanged(this); } - /** - * Gets the data organization object for this data type. The data organization has the alignment - * and size information for data types. - * @return the data organization - */ - protected DataOrganization getDataOrganization() { - if (dataMgr != null) { - DataOrganization dataOrganization = dataMgr.getDataOrganization(); - if (dataOrganization != null) { - return dataOrganization; - } - } - return DataOrganizationImpl.getDefaultOrganization(); - } - @Override public boolean isInternallyAligned() { int dbValue = record.getIntValue(CompositeDBAdapter.COMPOSITE_INTERNAL_ALIGNMENT_COL); @@ -593,12 +642,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite { protected void setAlignment(Composite composite, boolean notify) { doSetInternallyAligned(composite.isInternallyAligned()); - try { - doSetPackingValue(composite.getPackingValue()); - } - catch (InvalidInputException e) { - throw new AssertException("Got bad pack value from existing composite.", e); - } + doSetPackingValue(composite.getPackingValue()); if (composite.isDefaultAligned()) { doSetToDefaultAlignment(); @@ -607,12 +651,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite { doSetToMachineAlignment(); } else { - try { - doSetMinimumAlignment(composite.getMinimumAlignment()); - } - catch (InvalidInputException e) { - throw new AssertException("Got bad minimum alignment from existing composite.", e); - } + doSetMinimumAlignment(composite.getMinimumAlignment()); } adjustInternalAlignment(notify); } @@ -628,7 +667,8 @@ abstract class CompositeDB extends DataTypeDB implements Composite { @Override public int getAlignment() { - return getDataOrganization().getAlignment(this, getLength()); + // TODO: use cached value if available (requires DB change to facilitate) + return CompositeAlignmentHelper.getAlignment(getDataOrganization(), this); } /** @@ -639,7 +679,14 @@ abstract class CompositeDB extends DataTypeDB implements Composite { protected void dumpComponents(StringBuilder buffer, String pad) { for (DataTypeComponent dtc : getComponents()) { DataType dataType = dtc.getDataType(); + buffer.append(pad + dtc.getOffset()); buffer.append(pad + dataType.getDisplayName()); + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bfDt = (BitFieldDataType) dataType; + buffer.append("("); + buffer.append(Integer.toString(bfDt.getBitOffset())); + buffer.append(")"); + } buffer.append(pad + dtc.getLength()); buffer.append(pad + dtc.getFieldName()); String comment = dtc.getComment(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java index 7311771fbb..07be482c11 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java @@ -23,6 +23,7 @@ import java.io.IOException; import db.Record; import ghidra.docking.settings.Settings; import ghidra.program.model.data.*; +import ghidra.util.SystemUtilities; import ghidra.util.exception.DuplicateNameException; /** @@ -30,12 +31,14 @@ import ghidra.util.exception.DuplicateNameException; * component is for an undefined data type, then the record object is * null. */ -class DataTypeComponentDB implements DataTypeComponent { +class DataTypeComponentDB implements InternalDataTypeComponent { private final DataTypeManagerDB dataMgr; private final ComponentDBAdapter adapter; private final Record record; // null record -> undefined component private final Composite parent; + + private DataType cachedDataType; // required for bit-fields during packing process private boolean isFlexibleArrayComponent = false; private int ordinal; @@ -96,6 +99,24 @@ class DataTypeComponentDB implements DataTypeComponent { return isFlexibleArrayComponent; } + @Override + public boolean isBitFieldComponent() { + if (record == null) { + return false; + } + long id = record.getLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL); + return DataTypeManagerDB.getTableID(id) == DataTypeManagerDB.BITFIELD; + } + + @Override + public boolean isZeroBitFieldComponent() { + if (isBitFieldComponent()) { + BitFieldDataType bitField = (BitFieldDataType) getDataType(); + return bitField.getBitSize() == 0; + } + return false; + } + /** * Get record key * @return record key or -1 for undefined component without a record @@ -109,6 +130,9 @@ class DataTypeComponentDB implements DataTypeComponent { if (record == null) { return DataType.DEFAULT; } + if (cachedDataType != null) { + return cachedDataType; + } long id = record.getLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL); if (id == -1) { return DataType.DEFAULT; @@ -133,6 +157,13 @@ class DataTypeComponentDB implements DataTypeComponent { return offset; } + boolean containsOffset(int off) { + if (isFlexibleArrayComponent) { + return false; + } + return off >= offset && off <= (offset + length - 1); + } + @Override public int getOrdinal() { if (isFlexibleArrayComponent) { @@ -202,6 +233,9 @@ class DataTypeComponentDB implements DataTypeComponent { @Override public String getFieldName() { + if (isZeroBitFieldComponent()) { + return ""; + } if (record != null) { return record.getString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL); } @@ -210,6 +244,9 @@ class DataTypeComponentDB implements DataTypeComponent { @Override public String getDefaultFieldName() { + if (isZeroBitFieldComponent()) { + return ""; + } if (parent instanceof Structure) { return DEFAULT_FIELD_NAME_PREFIX + "_0x" + Integer.toHexString(getOffset()); } @@ -252,6 +289,12 @@ class DataTypeComponentDB implements DataTypeComponent { } } + @Override + public int hashCode() { + // It is not expected that these objects ever be put in a hash map + return super.hashCode(); + } + @Override public boolean equals(Object obj) { if (!(obj instanceof DataTypeComponent)) { @@ -261,11 +304,12 @@ class DataTypeComponentDB implements DataTypeComponent { DataType myDt = getDataType(); DataType otherDt = dtc.getDataType(); - // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from + // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from // stored values for flexible array component if (getOffset() != dtc.getOffset() || getLength() != dtc.getLength() || - getOrdinal() != dtc.getOrdinal() || !isSameString(getFieldName(), dtc.getFieldName()) || - !isSameString(getComment(), dtc.getComment())) { + getOrdinal() != dtc.getOrdinal() || + !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || + !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; } if (!(myDt instanceof Pointer) && !myDt.getPathName().equals(otherDt.getPathName())) { @@ -302,25 +346,39 @@ class DataTypeComponentDB implements DataTypeComponent { boolean aligned = (myParent instanceof Composite) ? ((Composite) myParent).isInternallyAligned() : false; // Components don't need to have matching offset when they are aligned, only matching ordinal. - // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from + // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from // stored values for flexible array component if ((!aligned && (getOffset() != dtc.getOffset())) || // Components don't need to have matching length when they are aligned. Is this correct? (!aligned && (getLength() != dtc.getLength())) || getOrdinal() != dtc.getOrdinal() || - !isSameString(getFieldName(), dtc.getFieldName()) || - !isSameString(getComment(), dtc.getComment())) { - + !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || + !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; } return DataTypeUtilities.isSameOrEquivalentDataType(myDt, otherDt); } - private boolean isSameString(String s1, String s2) { - if (s1 == null) { - return s2 == null; + @Override + public void update(int newOrdinal, int newOffset, int newLength) { + if (isFlexibleArrayComponent) { + return; + } + if (length < 0) { + throw new IllegalArgumentException( + "Cannot set data type component length to " + length + "."); + } + + ordinal = newOrdinal; + offset = newOffset; + length = newLength; + + if (record != null) { + record.setIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL, ordinal); + record.setIntValue(ComponentDBAdapter.COMPONENT_OFFSET_COL, offset); + record.setIntValue(ComponentDBAdapter.COMPONENT_SIZE_COL, length); + updateRecord(); } - return s1.equals(s2); } void setOffset(int newOffset, boolean updateRecord) { @@ -350,7 +408,7 @@ class DataTypeComponentDB implements DataTypeComponent { } void setLength(int length, boolean updateRecord) { - if (isFlexibleArrayComponent) { + if (isFlexibleArrayComponent || length == this.length) { return; } this.length = length; @@ -381,12 +439,9 @@ class DataTypeComponentDB implements DataTypeComponent { return record; } - /** - * @param oldDt - * @param newDt - * @return - */ - void setDataType(DataType newDt) { + @Override + public void setDataType(DataType newDt) { + // intended for internal use only if (record != null) { record.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL, dataMgr.getResolvedID(newDt)); @@ -404,10 +459,14 @@ class DataTypeComponentDB implements DataTypeComponent { StringBuffer buffer = new StringBuffer(); buffer.append(" " + getOrdinal()); buffer.append(" " + getOffset()); - buffer.append(" " + getDataType().getDisplayName()); + DataType dt = getDataType(); + buffer.append(" " + dt.getDisplayName()); if (isFlexibleArrayComponent) { buffer.append("[0]"); } + else if (dt instanceof BitFieldDataType) { + buffer.append("(" + ((BitFieldDataType) dt).getBitOffset() + ")"); + } buffer.append(" " + getLength()); buffer.append(" " + getFieldName()); String comment = getComment(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java index d16218e037..1a9d4d1add 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java @@ -17,6 +17,7 @@ package ghidra.program.database.data; import java.io.IOException; import java.net.URL; +import java.util.List; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -41,7 +42,7 @@ import ghidra.util.exception.NotYetImplementedException; abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeListener { protected Record record; - protected DataTypeManagerDB dataMgr; + protected final DataTypeManagerDB dataMgr; private volatile Settings defaultSettings; private final static SettingsDefinition[] EMPTY_DEFINITIONS = new SettingsDefinition[0]; protected boolean resolving; @@ -101,6 +102,11 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList */ protected abstract void setSourceArchiveID(UniversalID id); + @Override + public final DataOrganization getDataOrganization() { + return dataMgr.getDataOrganization(); + } + @Override protected boolean refresh() { category = null; @@ -241,8 +247,7 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList if (length < 0) { return 1; } - DataTypeManager dtm = getDataTypeManager(); - DataOrganization dataOrganization = dtm.getDataOrganization(); + DataOrganization dataOrganization = dataMgr.getDataOrganization(); return dataOrganization.getAlignment(this, length); } @@ -414,7 +419,7 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList */ @Override public void addParent(DataType dt) { - if (dt instanceof DataTypeDB) { + if (dt instanceof DataTypeDB && dt.getDataTypeManager() == dataMgr) { dataMgr.addParentChildRecord(((DataTypeDB) dt).key, key); } } @@ -424,31 +429,28 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList */ @Override public void removeParent(DataType dt) { - if (dt instanceof DataTypeDB) { + if (dt instanceof DataTypeDB && dt.getDataTypeManager() == dataMgr) { dataMgr.removeParentChildRecord(((DataTypeDB) dt).key, key); } } protected void notifySizeChanged() { - DataType[] dts = dataMgr.getParentDataTypes(key); - for (int i = 0; i < dts.length; i++) { - dts[i].dataTypeSizeChanged(this); + for (DataType dt : dataMgr.getParentDataTypes(key)) { + dt.dataTypeSizeChanged(this); } dataMgr.dataTypeChanged(this); } protected void notifyNameChanged(String oldName) { - DataType[] dts = dataMgr.getParentDataTypes(key); - for (int i = 0; i < dts.length; i++) { - dts[i].dataTypeNameChanged(this, oldName); + for (DataType dt : dataMgr.getParentDataTypes(key)) { + dt.dataTypeNameChanged(this, oldName); } dataMgr.dataTypeNameChanged(this, oldName); } protected void notifyDeleted() { - DataType[] dts = dataMgr.getParentDataTypes(key); - for (int i = 0; i < dts.length; i++) { - dts[i].dataTypeDeleted(this); + for (DataType dt : dataMgr.getParentDataTypes(key)) { + dt.dataTypeDeleted(this); } } @@ -457,7 +459,9 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList */ @Override public DataType[] getParents() { - return dataMgr.getParentDataTypes(key); + List parents = dataMgr.getParentDataTypes(key); + DataType[] array = new DataType[parents.size()]; + return parents.toArray(array); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index d000c7d03a..7d28efc84e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -58,6 +58,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { static final int FUNCTION_DEF = 6; static final int PARAMETER = 7; static final int ENUM = 8; + static final int BITFIELD = 9; // see BitFieldDataType - used for encoding only (no table) + + static final int DATA_TYPE_KIND_SHIFT = 56; private BuiltinDBAdapter builtinAdapter; private ComponentDBAdapter componentAdapter; @@ -695,6 +698,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (dataType == DataType.DEFAULT) { return dataType; } + if (dataType instanceof BitFieldDataType) { + return resolveBitFieldDataType((BitFieldDataType) dataType, handler); + } lock.acquire(); DataTypeConflictHandler originalHandler = null; try { @@ -757,6 +763,34 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return createDataType(dataType, BuiltInSourceArchive.INSTANCE); } + private DataType resolveBitFieldDataType(BitFieldDataType bitFieldDataType, + DataTypeConflictHandler handler) { + + // NOTE: When a bit-field is getting adding added it will get resolved more than once. + // The first time we will ensure that the base data type, which may be a TypeDef, gets + // resolved. If the bit-offset is too large it will be set to 0 + // with the expectation that it will get corrected during subsequent packing. + DataType baseDt = bitFieldDataType.getBaseDataType(); + DataType resolvedBaseDt = resolve(baseDt, handler); + int baseLength = resolvedBaseDt.getLength(); + int baseLengthBits = 8 * baseLength; + int bitSize = bitFieldDataType.getDeclaredBitSize(); + int bitOffset = bitFieldDataType.getBitOffset(); + int storageSize = bitFieldDataType.getStorageSize(); + int storageSizeBits = 8 * storageSize; + if ((bitOffset + bitSize) > storageSizeBits) { + // should get recomputed during packing when used within aligned structure + bitOffset = getDataOrganization().isBigEndian() ? baseLengthBits - bitSize : 0; + storageSize = baseLength; + } + try { + return new BitFieldDBDataType(resolvedBaseDt, bitSize, bitOffset, storageSize, this); + } + catch (InvalidDataTypeException e) { + throw new AssertException("unexpected", e); + } + } + /** * Either finds an equivalent dataType with the same categoryPath and name (or conflict name) * to the given dataType. Otherwise, it creates a new dataType in this archive equivalent to @@ -1305,6 +1339,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (dt == DataType.DEFAULT) { return DEFAULT_DATATYPE_ID; } + if (dt instanceof BitFieldDataType) { + return createKey(BITFIELD, BitFieldDBDataType.getId((BitFieldDataType) dt)); + } if (dt instanceof BadDataType) { return BAD_DATATYPE_ID; } @@ -1632,6 +1669,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { // an undo. So make sure it really is there. if (dataType instanceof DataTypeDB) { long id = ((DataTypeDB) dataType).getKey(); +// NOTE: Does not seem to help following an undo/redo +// DataTypeDB existingDt = dtCache.get(id); +// return existingDt == dataType && existingDt.validate(lock); +// return dtCache.get(id) != null; } return builtIn2IdMap.containsKey(dataType); @@ -1768,8 +1809,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } - private int getTableID(long dataID) { - return (int) (dataID >> 56); + static int getTableID(long dataID) { + return (int) (dataID >> DATA_TYPE_KIND_SHIFT); } private DataType getDataType(long dataTypeID, Record record) { @@ -1789,6 +1830,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return getFunctionDefDataType(dataTypeID, record); case ENUM: return getEnumDataType(dataTypeID, record); + case BITFIELD: + return BitFieldDBDataType.getBitFieldDataType(dataTypeID, this); default: return null; } @@ -2108,6 +2151,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { category.dataTypeAdded(structDB); structDB.doReplaceWith(struct, false, handler); + structDB.setDescription(struct.getDescription()); structDB.notifySizeChanged(); // doReplaceWith updated the last change time so set it back to what we want. structDB.setLastChangeTime(struct.getLastChangeTime()); @@ -2176,6 +2220,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { category.dataTypeAdded(unionDB); unionDB.doReplaceWith(union, false, handler); + unionDB.setDescription(union.getDescription()); unionDB.notifySizeChanged(); // doReplaceWith updated the last change time so set it back to what we want. unionDB.setLastChangeTime(union.getLastChangeTime()); @@ -3115,7 +3160,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * the bits are from the tableKey. */ static long createKey(int tableID, long tableKey) { - long key = (long) tableID << 56; + long key = (long) tableID << DATA_TYPE_KIND_SHIFT; return key |= tableKey; } @@ -3152,21 +3197,46 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } - DataType[] getParentDataTypes(long childID) { + List getParentDataTypes(long childID) { + lock.acquire(); try { long[] ids = parentChildAdapter.getParentIds(childID); - DataType[] dts = new DataType[ids.length]; - for (int i = 0; i < dts.length; i++) { - dts[i] = getDataType(ids[i]); + // TODO: consider deduping ids using Set + List dts = new ArrayList<>(); + for (int i = 0; i < ids.length; i++) { + DataType dt = getDataType(ids[i]); + if (dt == null) { + // cleanup invalid records for missing parent + attemptRecordRemovalForParent(ids[i]); + } + else { + dts.add(dt); + } } return dts; + } catch (IOException e) { dbError(e); } + finally { + lock.release(); + } return null; } + private void attemptRecordRemovalForParent(long parentKey) throws IOException { + lock.acquire(); + try { + if (dbHandle.isTransactionActive()) { + parentChildAdapter.removeAllRecordsForParent(parentKey); + } + } + finally { + lock.release(); + } + } + @Override public Set getDataTypesContaining(DataType dataType) { Set set = new HashSet<>(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java index 2478d67fea..c01cca3b50 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java @@ -124,6 +124,10 @@ public class DataTypeUtilities { else if (dt instanceof BuiltInDataType) { // no-op; prevents assert exception below } + else if (dt instanceof BitFieldDataType) { + BitFieldDataType bitFieldDt = (BitFieldDataType) dt; + list.add(bitFieldDt.getBaseDataType()); + } else if (dt instanceof MissingBuiltInDataType) { // no-op; prevents assert exception below } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java index f02c8a47ca..b238a1029f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java @@ -16,6 +16,7 @@ package ghidra.program.database.data; import java.io.IOException; +import java.math.BigInteger; import java.util.*; import db.Record; @@ -435,11 +436,7 @@ class EnumDB extends DataTypeDB implements Enum { value = buf.getLong(0); break; } - String valueName = getName(value); - if (valueName == null) { - valueName = getCompoundValue(value); - } - return valueName; + return getRepresentation(value); } catch (MemoryAccessException e) { return "??"; @@ -449,6 +446,19 @@ class EnumDB extends DataTypeDB implements Enum { } } + @Override + public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength) { + return getRepresentation(bigInt.longValue()); + } + + private String getRepresentation(long value) { + String valueName = getName(value); + if (valueName == null) { + valueName = getCompoundValue(value); + } + return valueName; + } + private String getCompoundValue(long value) { if (value == 0) { return "0"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java index 0c43501955..34c33261bf 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java @@ -70,6 +70,7 @@ public class ProgramDataTypeManager extends DataTypeManagerDB @Override public void setProgram(ProgramDB p) { this.program = p; + dataOrganization = p.getCompilerSpec().getDataOrganization(); removeOldFileNameList(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java index 415f1f3dd5..bf3e6767a2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java @@ -35,8 +35,8 @@ import ghidra.util.task.TaskMonitor; /** * Class for managing data types in a project archive */ -public class ProjectDataTypeManager extends DataTypeManagerDB implements - ProjectArchiveBasedDataTypeManager { +public class ProjectDataTypeManager extends DataTypeManagerDB + implements ProjectArchiveBasedDataTypeManager { // private static final String DT_ARCHIVE_FILENAMES = "DataTypeArchiveFilenames"; // private static final String FILENAME_SEPARATOR = ";"; @@ -57,9 +57,8 @@ public class ProjectDataTypeManager extends DataTypeManagerDB implements * @throws VersionException if the database does not match the expected version. * @throws IOException if a database I/O error occurs. */ - public ProjectDataTypeManager(DBHandle handle, int openMode, ErrorHandler errHandler, - Lock lock, TaskMonitor monitor) throws CancelledException, VersionException, - IOException { + public ProjectDataTypeManager(DBHandle handle, int openMode, ErrorHandler errHandler, Lock lock, + TaskMonitor monitor) throws CancelledException, VersionException, IOException { super(handle, null, openMode, errHandler, lock, monitor); } @@ -252,16 +251,6 @@ public class ProjectDataTypeManager extends DataTypeManagerDB implements return ArchiveType.PROJECT; } - @Override - public DataOrganization getDataOrganization() { - if (dataOrganization == null) { - dataOrganization = DataOrganizationImpl.getDefaultOrganization(); - } - return dataOrganization; -// // For now project data type archive will use the default data organization. -// return DataOrganization.getDefaultOrganization(); - } - public void archiveReady(int openMode, TaskMonitor monitor) throws CancelledException { if (openMode == DBConstants.UPGRADE) { doSourceArchiveUpdates(null, monitor); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java index f326eee74c..8e6c874d97 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.data; -import ghidra.app.plugin.core.datamgr.archive.SourceArchive; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import java.util.Iterator; import java.util.List; import db.*; +import ghidra.app.plugin.core.datamgr.archive.SourceArchive; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the data type archive identifier table. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java index f6fa4c16ff..f22fa3433a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package ghidra.program.database.data; -import ghidra.app.plugin.core.datamgr.archive.SourceArchive; -import ghidra.program.model.data.DataTypeManager; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.*; import db.*; +import ghidra.app.plugin.core.datamgr.archive.SourceArchive; +import ghidra.program.model.data.DataTypeManager; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; /** * Version 0 implementation for accessing the Data Type Archive ID database table. @@ -39,8 +37,8 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { static final Schema V0_SCHEMA = new Schema(VERSION, "Archive ID", new Class[] { StringField.class, StringField.class, ByteField.class, LongField.class, - BooleanField.class }, new String[] { "Domain File ID", "Name", "Type", - "Last Sync Time", "Dirty Flag" }); + BooleanField.class }, + new String[] { "Domain File ID", "Name", "Type", "Last Sync Time", "Dirty Flag" }); private Table table; @@ -51,8 +49,8 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public SourceArchiveAdapterV0(DBHandle handle, boolean create) throws VersionException, - IOException { + public SourceArchiveAdapterV0(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { table = handle.createTable(TABLE_NAME, V0_SCHEMA); @@ -66,9 +64,8 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + TABLE_NAME + " but got " + - table.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + TABLE_NAME + + " but got " + table.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -112,7 +109,7 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { @Override public List getRecords() throws IOException { - List records = new ArrayList(); + List records = new ArrayList<>(); RecordIterator iterator = table.iterator(); while (iterator.hasNext()) { records.add(iterator.next()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index 6eabe19833..9939f0952e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -22,6 +22,7 @@ import db.Record; import ghidra.docking.settings.Settings; import ghidra.program.database.DBObjectCache; import ghidra.program.model.data.*; +import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult; import ghidra.program.model.mem.MemBuffer; import ghidra.util.Msg; import ghidra.util.exception.InvalidInputException; @@ -35,10 +36,13 @@ class StructureDB extends CompositeDB implements Structure { private static OrdinalComparator ordinalComparator = new OrdinalComparator(); private static OffsetComparator offsetComparator = new OffsetComparator(); private static ComponentComparator componentComparator = new ComponentComparator(); + protected static Comparator bitOffsetComparatorLE = new BitOffsetComparator(false); + protected static Comparator bitOffsetComparatorBE = new BitOffsetComparator(true); private int structLength; private int numComponents; // If aligned, this does not include the undefined data types. private ArrayList components; private DataTypeComponentDB flexibleArrayComponent; + private int alignment = -1; /** * Constructor @@ -61,8 +65,8 @@ class StructureDB extends CompositeDB implements Structure { try { long[] ids = componentAdapter.getComponentIdsInComposite(key); - for (int i = 0; i < ids.length; i++) { - Record rec = componentAdapter.getRecord(ids[i]); + for (long id : ids) { + Record rec = componentAdapter.getRecord(id); DataTypeComponentDB component = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); if (component.isFlexibleArrayComponent()) { @@ -82,9 +86,6 @@ class StructureDB extends CompositeDB implements Structure { numComponents = record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL); } - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.util.settings.Settings, int) - */ @Override public String getRepresentation(MemBuffer buf, Settings settings, int length) { if (isNotYetDefined()) { @@ -93,63 +94,51 @@ class StructureDB extends CompositeDB implements Structure { return ""; } - /** - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ - public void add(DataType dataType, int length, String name, String comment, int numCopies) { - lock.acquire(); - try { - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } - for (int ii = 0; ii < numCopies; ++ii) { - doAdd(dataType, length, name, comment, false, false); - } - adjustInternalAlignment(false); - notifySizeChanged(); - } - finally { - lock.release(); - } - } - @Override public DataTypeComponent add(DataType dataType, int length, String name, String comment) { - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } - return doAdd(dataType, length, name, comment, true, true); + return doAdd(dataType, length, false, name, comment, true); } - private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment, - boolean notify, boolean align) { + private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray, + String name, String comment, boolean alignAndNotify) { lock.acquire(); try { checkDeleted(); + validateDataType(dataType); + dataType = resolve(dataType); checkAncestry(dataType); DataTypeComponentDB dtc = null; try { - boolean isFlexibleArray = false; - if (dataType == DataType.DEFAULT) { + if (dataType == DataType.DEFAULT && !isFlexibleArray) { dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, key, numComponents, structLength); } else { + int offset = structLength; int ordinal = numComponents; - isFlexibleArray = (length == 0); - if (length == 0) { + + int componentLength; + if (isFlexibleArray) { // assume trailing flexible array component offset = -1; ordinal = -1; - isFlexibleArray = true; - clearFlexibleArrayComponent(); + if (flexibleArrayComponent != null) { + flexibleArrayComponent.getDataType().removeParent(this); + componentAdapter.removeRecord(flexibleArrayComponent.getKey()); + flexibleArrayComponent = null; + } + componentLength = 0; } + else { + componentLength = getPreferredComponentLength(dataType, length); + } + Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, - length, ordinal, offset, name, comment); + componentLength, ordinal, offset, name, comment); dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); dataType.addParent(this); if (isFlexibleArray) { @@ -160,16 +149,20 @@ class StructureDB extends CompositeDB implements Structure { } } if (!isFlexibleArray) { + + int structureGrowth = dtc.getLength(); + if (!isInternallyAligned() && length > 0) { + structureGrowth = length; + } + record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, ++numComponents); - structLength += dtc.getLength(); + structLength += structureGrowth; record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); compositeAdapter.updateRecord(record, true); } - if (align) { + if (alignAndNotify) { adjustInternalAlignment(false); - } - if (notify) { notifySizeChanged(); } } @@ -183,10 +176,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * - * @see ghidra.program.model.data.Structure#growStructure(int) - */ @Override public void growStructure(int amount) { lock.acquire(); @@ -218,9 +207,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.EditableComposite#insert(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name, String comment) { @@ -234,11 +220,10 @@ class StructureDB extends CompositeDB implements Structure { return add(dataType, length, name, comment); } validateDataType(dataType); - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } + dataType = resolve(dataType); checkAncestry(dataType); + int idx; if (isInternallyAligned()) { idx = ordinal; @@ -250,16 +235,13 @@ class StructureDB extends CompositeDB implements Structure { idx = -idx - 1; } if (dataType == DataType.DEFAULT) { + // assume unaligned insert of DEFAULT shiftOffsets(idx, 1, 1); notifySizeChanged(); return getComponent(ordinal); } - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } + length = getPreferredComponentLength(dataType, length); int offset = getComponent(ordinal).getOffset(); Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length, @@ -282,30 +264,166 @@ class StructureDB extends CompositeDB implements Structure { } @Override - public void insert(int ordinal, DataType dataType, int length, String name, String comment, - int numCopies) { + public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { + lock.acquire(); try { checkDeleted(); if (ordinal < 0 || ordinal > numComponents) { throw new ArrayIndexOutOfBoundsException(ordinal); } - if (ordinal == numComponents) { - add(dataType, length, name, comment, numCopies); - return; - } - for (int ii = 0; ii < numCopies; ++ii) { - insert(ordinal + ii, dataType, length, name, comment); + + if (!isInternallyAligned()) { + int offset = structLength; + if (ordinal < numComponents) { + offset = getComponent(ordinal).getOffset(); + } + return insertBitFieldAt(offset, byteWidth, bitOffset, baseDataType, bitSize, + componentName, comment); } + + // handle aligned bitfield insertion + BitFieldDataType bitFieldDt = + new BitFieldDBDataType(baseDataType, bitSize, 0, 0, dataMgr); + return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); } finally { lock.release(); } } - /** - * @see ghidra.program.model.data.EditableComposite#delete(int) - */ + @Override + public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException { + lock.acquire(); + try { + checkDeleted(); + BitFieldDataType.checkBaseDataType(baseDataType); + baseDataType = baseDataType.clone(getDataTypeManager()); + + if (byteOffset < 0 || bitSize < 0) { + throw new IllegalArgumentException( + "Negative values not permitted when defining bitfield"); + } + if (byteWidth <= 0) { + throw new IllegalArgumentException("Invalid byteWidth"); + } + + int effectiveBitSize = + BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength()); + + int minByteWidth = BitFieldDataType.getMinimumStorageSize(effectiveBitSize + bitOffset); + if (byteWidth < minByteWidth) { + throw new IllegalArgumentException( + "Bitfield does not fit within specified constraints"); + } + + boolean bigEndian = getDataOrganization().isBigEndian(); + + boolean hasConflict = false; + int additionalShift = 0; + + int startBitOffset = BitOffsetComparator.getNormalizedBitfieldOffset(byteOffset, + byteWidth, effectiveBitSize, bitOffset, bigEndian); + + Comparator bitOffsetComparator = + bigEndian ? bitOffsetComparatorBE : bitOffsetComparatorLE; + int startIndex = Collections.binarySearch(components, new Integer(startBitOffset), + bitOffsetComparator); + if (startIndex < 0) { + startIndex = -startIndex - 1; + } + else { + hasConflict = true; + DataTypeComponentDB dtc = components.get(startIndex); + if (bitSize == 0 || dtc.isZeroBitFieldComponent()) { + hasConflict = dtc.getOffset() != (startBitOffset / 8); + } + if (hasConflict) { + additionalShift = byteOffset - dtc.getOffset(); + } + } + + int ordinal; // computed ordinal will be adjusted after insertion complete + if (startIndex < components.size()) { + DataTypeComponentDB dtc = components.get(startIndex); + ordinal = dtc.getOrdinal(); + } + else { + ordinal = startIndex; + } + + if (isInternallyAligned()) { + insertBitField(ordinal, 0, 0, baseDataType, effectiveBitSize, componentName, + comment); + } + + int endIndex = startIndex; + if (startIndex < components.size()) { + // some shifting of components may be required + int endBitOffset = startBitOffset + effectiveBitSize - 1; + endIndex = Collections.binarySearch(components, new Integer(endBitOffset), + bitOffsetComparator); + if (endIndex < 0) { + endIndex = -endIndex - 1; + } + else { + hasConflict = true; + } + } + + if (startIndex != endIndex) { + hasConflict = true; + } + + // Any conflict will force a full insertion of byteWidth + if (hasConflict) { + shiftOffsets(startIndex, 1, byteWidth + additionalShift); + } + + int requiredLength = byteOffset + byteWidth; + if (requiredLength > structLength) { + structLength = requiredLength; + } + + // use minimal storage + int storageBitOffset = bitOffset % 8; + int storageSize = + BitFieldDataType.getMinimumStorageSize(effectiveBitSize + storageBitOffset); + + int revisedOffset; + if (bigEndian) { + revisedOffset = byteOffset + byteWidth - ((effectiveBitSize + bitOffset + 7) / 8); + } + else { + revisedOffset = byteOffset + (bitOffset / 8); + } + + BitFieldDataType bitfieldDt = new BitFieldDBDataType(baseDataType, bitSize, + storageBitOffset, storageSize, getDataTypeManager()); + + Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(bitfieldDt), key, + storageSize, ordinal, revisedOffset, componentName, comment); + DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); + bitfieldDt.addParent(this); // has no affect + components.add(startIndex, dtc); + + adjustUnalignedComponents(); + notifySizeChanged(); + return dtc; + } + catch (IOException e) { + dataMgr.dbError(e); + } + finally { + lock.release(); + } + return null; + } + @Override public void delete(int ordinal) { lock.acquire(); @@ -322,21 +440,14 @@ class StructureDB extends CompositeDB implements Structure { idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); } if (idx >= 0) { - DataTypeComponentDB dtc = components.remove(idx); - dtc.getDataType().removeParent(this); - try { - componentAdapter.removeRecord(dtc.getKey()); - } - catch (IOException e) { - dataMgr.dbError(e); - } - shiftOffsets(idx, -1, -dtc.getLength()); - adjustInternalAlignment(true); - notifySizeChanged(); - return; + doDelete(idx); + adjustInternalAlignment(false); + } + else { + // assume unaligned removal of DEFAULT + idx = -idx - 1; + shiftOffsets(idx, -1, -1); } - idx = -idx - 1; - shiftOffsets(idx, -1, -1); notifySizeChanged(); } finally { @@ -344,6 +455,26 @@ class StructureDB extends CompositeDB implements Structure { } } + private void doDelete(int index) { + DataTypeComponentDB dtc = components.remove(index); + dtc.getDataType().removeParent(this); + try { + componentAdapter.removeRecord(dtc.getKey()); + } + catch (IOException e) { + dataMgr.dbError(e); + } + if (isInternallyAligned()) { + return; + } + int shiftAmount = 0; + // Bitfields: do not remove space previously occupied + if (!dtc.isBitFieldComponent()) { + shiftAmount = dtc.getLength(); + } + shiftOffsets(index, -1, -shiftAmount); + } + @Override public void delete(int[] ordinals) { lock.acquire(); @@ -355,7 +486,13 @@ class StructureDB extends CompositeDB implements Structure { } } - for (int ordinal : ordinals) { + // delete ordinals in reverse order so that they remain valid + // during individual deletes + int[] sortedOrdinals = ordinals.clone(); + Arrays.sort(sortedOrdinals); + + for (int i = sortedOrdinals.length - 1; i >= 0; i--) { + int ordinal = sortedOrdinals[i]; int idx; if (isInternallyAligned()) { idx = ordinal; @@ -365,18 +502,10 @@ class StructureDB extends CompositeDB implements Structure { ordinalComparator); } if (idx >= 0) { - DataTypeComponentDB dtc = components.remove(idx); - dtc.getDataType().removeParent(this); - try { - componentAdapter.removeRecord(dtc.getKey()); - } - catch (IOException e) { - dataMgr.dbError(e); - } - shiftOffsets(idx, -1, -dtc.getLength()); - + doDelete(idx); } else { + // assume unaligned removal of DEFAULT idx = -idx - 1; shiftOffsets(idx, -1, -1); } @@ -389,9 +518,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.EditableComposite#isPartOf(ghidra.program.model.data.DataType) - */ @Override public boolean isPartOf(DataType dataType) { lock.acquire(); @@ -419,9 +545,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Composite#getNumComponents(ghidra.program.model.mem.MemBuffer) - */ @Override public int getNumComponents() { lock.acquire(); @@ -445,9 +568,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Composite#getComponent(int, ghidra.program.model.mem.MemBuffer) - */ @Override public DataTypeComponent getComponent(int ordinal) { lock.acquire(); @@ -523,9 +643,18 @@ class StructureDB extends CompositeDB implements Structure { return structLength == 0 && flexibleArrayComponent == null; } - /** - * @see ghidra.program.model.data.DataType#getLength() - */ + @Override + public int getAlignment() { + if (!isInternallyAligned()) { + return 1; // Unaligned + } + if (alignment <= 0) { + StructurePackResult packResult = AlignedStructureInspector.packComponents(this); + alignment = packResult.alignment; + } + return alignment; + } + @Override public int getLength() { lock.acquire(); @@ -541,9 +670,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Structure#clearComponent(int) - */ @Override public void clearComponent(int ordinal) { lock.acquire(); @@ -582,8 +708,52 @@ class StructureDB extends CompositeDB implements Structure { } /** - * @see ghidra.program.model.data.Structure#deleteAtOffset(int) + * Backup from specified ordinal to the first component which contains + * the specified offset. For normal components the specified + * ordinal will be returned, however for bit-fields the ordinal of the first + * bit-field containing the specified offset will be returned. + * @param ordinal component ordinal + * @param offset offset within structure + * @return index of first defined component containing specific offset. */ + private int backupToFirstComponentContainingOffset(int index, int offset) { + if (index == 0) { + return 0; + } + DataTypeComponentDB dtc = components.get(index); + while (index != 0 && dtc.isBitFieldComponent()) { + DataTypeComponentDB previous = components.get(index - 1); + if (!previous.containsOffset(offset)) { + break; + } + dtc = previous; + --index; + } + return index; + } + + /** + * Advance from specified ordinal to the last component which contains + * the specified offset. For normal components the specified + * ordinal will be returned, however for bit-fields the ordinal of the last + * bit-field containing the specified offset will be returned. + * @param ordinal component ordinal + * @param offset offset within structure + * @return index of last defined component containing specific offset. + */ + private int advanceToLastComponentContainingOffset(int index, int offset) { + DataTypeComponentDB dtc = components.get(index); + while (index < (components.size() - 1) && dtc.isBitFieldComponent()) { + DataTypeComponentDB next = components.get(index + 1); + if (!next.containsOffset(offset)) { + break; + } + dtc = next; + ++index; + } + return index; + } + @Override public void deleteAtOffset(int offset) { lock.acquire(); @@ -592,24 +762,31 @@ class StructureDB extends CompositeDB implements Structure { if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } + if (offset >= structLength) { + return; + } int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); - int delta = -1; + int offsetDelta = 0; + int ordinalDelta = 0; if (index < 0) { index = -index - 1; + --ordinalDelta; + offsetDelta = -1; + shiftOffsets(index, ordinalDelta, offsetDelta); } else { - DataTypeComponentDB dtc = components.remove(index); - dtc.getDataType().removeParent(this); - try { - componentAdapter.removeRecord(dtc.getKey()); + index = advanceToLastComponentContainingOffset(index, offset); + DataTypeComponentDB dtc = components.get(index); + while (dtc.containsOffset(offset)) { + doDelete(index); + if (--index < 0) { + break; + } + dtc = components.get(index); } - catch (IOException e) { - dataMgr.dbError(e); - } - delta = -dtc.getLength(); } - shiftOffsets(index, -1, delta); + adjustInternalAlignment(true); notifySizeChanged(); } @@ -618,9 +795,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Structure#getComponentAt(int) - */ @Override public DataTypeComponent getComponentAt(int offset) { lock.acquire(); @@ -631,7 +805,12 @@ class StructureDB extends CompositeDB implements Structure { } int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); if (index >= 0) { - return components.get(index); + DataTypeComponent dtc = components.get(index); + if (dtc.isBitFieldComponent()) { + index = backupToFirstComponentContainingOffset(index, offset); + dtc = components.get(index); + } + return dtc; } else if (isInternallyAligned()) { return null; @@ -649,9 +828,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Structure#getDataTypeAt(int) - */ @Override public DataTypeComponent getDataTypeAt(int offset) { lock.acquire(); @@ -670,9 +846,6 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Structure#getDefinedComponents() - */ @Override public DataTypeComponent[] getDefinedComponents() { lock.acquire(); @@ -685,9 +858,11 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Structure#insertAtOffset(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ + @Override + public final DataTypeComponent insertAtOffset(int offset, DataType dataType, int length) { + return insertAtOffset(offset, dataType, length, null, null); + } + @Override public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name, String comment) { @@ -698,9 +873,7 @@ class StructureDB extends CompositeDB implements Structure { throw new IllegalArgumentException("Offset cannot be negative."); } validateDataType(dataType); - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } + dataType = resolve(dataType); checkAncestry(dataType); @@ -713,6 +886,7 @@ class StructureDB extends CompositeDB implements Structure { int additionalShift = 0; if (index >= 0) { + index = backupToFirstComponentContainingOffset(index, offset); DataTypeComponentDB dtc = components.get(index); additionalShift = offset - dtc.getOffset(); } @@ -727,6 +901,7 @@ class StructureDB extends CompositeDB implements Structure { } if (dataType == DataType.DEFAULT) { + // assume unaligned insert of DEFAULT shiftOffsets(index, 1 + additionalShift, 1 + additionalShift); adjustInternalAlignment(true); notifySizeChanged(); @@ -734,11 +909,7 @@ class StructureDB extends CompositeDB implements Structure { offset); } - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } + length = getPreferredComponentLength(dataType, length); Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length, ordinal, offset, name, comment); @@ -759,17 +930,6 @@ class StructureDB extends CompositeDB implements Structure { return null; } - /** - * @see ghidra.program.model.data.Structure#insertAtOffset(int, ghidra.program.model.data.DataType) - */ - @Override - public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length) { - return insertAtOffset(offset, dataType, length, null, null); - } - - /** - * @see ghidra.program.model.data.Structure#replace(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name, String comment) { @@ -781,17 +941,23 @@ class StructureDB extends CompositeDB implements Structure { throw new ArrayIndexOutOfBoundsException(ordinal); } validateDataType(dataType); - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); + + DataTypeComponent origDtc = getComponent(ordinal); + if (origDtc.isBitFieldComponent()) { + throw new IllegalArgumentException( + "Bit-field component may not be directly replaced"); } + if (dataType == DataType.DEFAULT) { clearComponent(ordinal); return getComponent(ordinal); } + dataType = resolve(dataType); checkAncestry(dataType); - DataTypeComponent origDtc = getComponent(ordinal); + length = getPreferredComponentLength(dataType, length); + DataTypeComponent replaceComponent = replaceComponent(origDtc, dataType, length, name, comment, true); adjustInternalAlignment(true); @@ -802,29 +968,50 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.Structure#replace(int, ghidra.program.model.data.DataType) - */ @Override - public DataTypeComponent replace(int ordinal, DataType dataType, int length) { + public final DataTypeComponent replace(int ordinal, DataType dataType, int length) { return replace(ordinal, dataType, length, null, null); } - /** - * @see ghidra.program.model.data.Structure#replaceAtOffset(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name, String comment) { + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); + } if (offset >= getLength()) { throw new IllegalArgumentException( - "Can't replace at an offset that doesn't exist in the structure"); + "Offset " + offset + " is beyond end of structure (" + structLength + ")."); + } + if (dataType instanceof BitFieldDataType) { + throw new IllegalArgumentException("Components may not be replaced with a bit-field"); } lock.acquire(); try { checkDeleted(); + + validateDataType(dataType); + + DataTypeComponent origDtc = getComponentAt(offset); + if (origDtc.isBitFieldComponent()) { + throw new IllegalArgumentException( + "Bit-field component may not be directly replaced"); + } + + if (dataType == DataType.DEFAULT) { + int ordinal = origDtc.getOrdinal(); + clearComponent(ordinal); + return getComponent(ordinal); + } + + dataType = resolve(dataType); + checkAncestry(dataType); + + length = getPreferredComponentLength(dataType, length); + DataTypeComponent replaceComponent = - doReplace(offset, dataType, length, name, comment, true); + replaceComponent(origDtc, dataType, length, name, comment, true); + adjustInternalAlignment(true); return replaceComponent; } @@ -849,16 +1036,7 @@ class StructureDB extends CompositeDB implements Structure { if (!(dataType instanceof Structure)) { throw new IllegalArgumentException(); } - doReplaceWith((Structure) dataType, true); - } - - /** - * - * @param struct - * @param notify - */ - void doReplaceWith(Structure struct, boolean notify) { - doReplaceWith(struct, notify, null); + doReplaceWith((Structure) dataType, true, null); } /** @@ -871,8 +1049,10 @@ class StructureDB extends CompositeDB implements Structure { lock.acquire(); try { checkDeleted(); + int oldLength = structLength; - long oldMinAlignment = getMinimumAlignment(); + int oldMinAlignment = getMinimumAlignment(); + for (int i = 0; i < components.size(); i++) { DataTypeComponentDB dtc = components.get(i); dtc.getDataType().removeParent(this); @@ -892,56 +1072,20 @@ class StructureDB extends CompositeDB implements Structure { structLength = struct.getLength(); numComponents = isInternallyAligned() ? 0 : structLength; } + setAlignment(struct, false); - setDescription(struct.getDescription()); - - DataTypeComponent[] otherComponents = struct.getDefinedComponents(); - for (int i = 0; i < otherComponents.length; i++) { - DataTypeComponent dtc = otherComponents[i]; - DataType dt = dtc.getDataType(); - if (dt == null) { - Msg.warn(this, - "Data type IS NULL: in " + struct.getPathName() + " component " + i); - continue; // dt can be null if the dt isDeleted by an Undo. - } - dt = resolve(dt, handler); - if (isInternallyAligned()) { - doAdd(dt, dtc.getLength(), dtc.getFieldName(), dtc.getComment(), false, true); - } - else { - // TODO Is this the right way to get length? - doReplace(dtc.getOffset(), dt, dtc.getLength(), dtc.getFieldName(), - dtc.getComment(), false); - } + if (struct.isInternallyAligned()) { + doReplaceWithAligned(struct); } - // ok now that all components have been laid down, see if we can make any of them bigger - // without affecting any offsets - for (int i = 0; i < components.size(); i++) { - DataTypeComponent dtc = components.get(i); - DataType dataType = dtc.getDataType(); - if (dataType.getLength() > dtc.getLength()) { - int n = consumeBytesAfter(i, dataType.getLength() - dtc.getLength()); - if (n > 0) { - shiftOffsets(i + 1, 0 - n, 0); - } - } + else { + doReplaceWithUnaligned(struct); } DataTypeComponent flexComponent = struct.getFlexibleArrayComponent(); if (flexComponent != null) { - // set flexible array component - DataType dt = flexComponent.getDataType(); - if (dt == null) { - // dt can be null if the dt isDeleted by an Undo. - Msg.warn(this, "Data type IS NULL: in " + struct.getPathName() + - " flexible array component"); - } - else { - dt = resolve(dt, handler); - doAdd(dt, 0, flexComponent.getFieldName(), flexComponent.getComment(), false, - true); - } + setFlexibleArrayComponent(flexComponent.getDataType(), flexComponent.getFieldName(), + flexComponent.getComment()); } record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); @@ -969,45 +1113,85 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType) - */ + private void doReplaceWithAligned(Structure struct) { + // assumes components is clear and that alignment characteristics have been set + DataTypeComponent[] otherComponents = struct.getDefinedComponents(); + for (int i = 0; i < otherComponents.length; i++) { + DataTypeComponent dtc = otherComponents[i]; + DataType dt = dtc.getDataType(); + int length = (dt instanceof Dynamic) ? dtc.getLength() : -1; + doAdd(dt, length, false, dtc.getFieldName(), dtc.getComment(), false); + } + adjustInternalAlignment(false); + dataMgr.dataTypeChanged(this); + } + + private void doReplaceWithUnaligned(Structure struct) throws IOException { + // assumes components is clear and that alignment characteristics have been set. + + // NOTE: unaligned bitfields should remain unchanged when + // transitioning endianess even though it makes little sense. + // Unaligned structures are not intended to be portable! + + DataTypeComponent[] otherComponents = struct.getDefinedComponents(); + for (int i = 0; i < otherComponents.length; i++) { + DataTypeComponent dtc = otherComponents[i]; + DataType dt = resolve(dtc.getDataType()); + checkAncestry(dt); + + int length = getPreferredComponentLength(dt, dtc.getLength()); + + Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dt), key, length, + dtc.getOrdinal(), dtc.getOffset(), dtc.getFieldName(), dtc.getComment()); + dt.addParent(this); + DataTypeComponentDB newDtc = + new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); + components.add(newDtc); + } + adjustComponents(false); + } + @Override public void dataTypeDeleted(DataType dt) { lock.acquire(); try { checkDeleted(); boolean didChange = false; + if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == dt) { + flexibleArrayComponent.getDataType().removeParent(this); + componentAdapter.removeRecord(flexibleArrayComponent.getKey()); + flexibleArrayComponent = null; + didChange = true; + } int n = components.size(); for (int i = n - 1; i >= 0; i--) { DataTypeComponentDB dtc = components.get(i); - if (dtc.getDataType() == dt) { + boolean removeBitFieldComponent = false; + if (dtc.isBitFieldComponent()) { + BitFieldDataType bitfieldDt = (BitFieldDataType) dtc.getDataType(); + removeBitFieldComponent = bitfieldDt.getBaseDataType() == dt; + } + if (removeBitFieldComponent || dtc.getDataType() == dt) { dt.removeParent(this); components.remove(i); - shiftOffsets(i, dtc.getLength() - 1, 0); - try { - componentAdapter.removeRecord(dtc.getKey()); - } - catch (IOException e) { - dataMgr.dbError(e); - } + shiftOffsets(i, dtc.getLength() - 1, 0); // ordinals only + componentAdapter.removeRecord(dtc.getKey()); didChange = true; } } if (didChange) { adjustInternalAlignment(true); - dataMgr.dataTypeChanged(this); + notifySizeChanged(); } } + catch (IOException e) { + dataMgr.dbError(e); + } finally { lock.release(); } } - /* - * (non-Javadoc) - * @see ghidra.program.database.data.DataTypeDB#dataTypeSizeChanged(ghidra.program.model.data.DataType) - */ @Override public void dataTypeSizeChanged(DataType dt) { lock.acquire(); @@ -1017,23 +1201,26 @@ class StructureDB extends CompositeDB implements Structure { adjustInternalAlignment(true); return; } - int n = components.size(); boolean didChange = false; + int n = components.size(); for (int i = 0; i < n; i++) { DataTypeComponentDB dtc = components.get(i); + int nextIndex = i + 1; if (dtc.getDataType() == dt) { + // assume no impact to bitfields since base types + // should not change size int dtLen = dt.getLength(); int dtcLen = dtc.getLength(); if (dtLen < dtcLen) { dtc.setLength(dtLen, true); - shiftOffsets(i + 1, dtcLen - dtLen, 0); + shiftOffsets(nextIndex, dtcLen - dtLen, 0); didChange = true; } else if (dtLen > dtcLen) { int consumed = consumeBytesAfter(i, dtLen - dtcLen); if (consumed > 0) { dtc.updateRecord(); - shiftOffsets(i + 1, -consumed, 0); + shiftOffsets(nextIndex, -consumed, 0); didChange = true; } } @@ -1051,49 +1238,16 @@ class StructureDB extends CompositeDB implements Structure { @Override public void dataTypeAlignmentChanged(DataType dt) { - // TODO lock.acquire(); try { checkDeleted(); -// if (isInternallyAligned()) { adjustInternalAlignment(true); -// return; -// } -// int n = components.size(); -// boolean didChange = false; -// for(int i=0;i dtcLen) { -// int consumed = consumeBytesAfter(i, dtLen-dtcLen); -// if (consumed > 0) { -// dtc.updateRecord(); -// shiftOffsets(i+1, -consumed, 0); -// didChange = true; -// } -// } -// } -// } -// if (didChange) { -// notifySizeChanged(); -// } } finally { lock.release(); } } - /** - * @see ghidra.program.model.data.DataType#isEquivalent(ghidra.program.model.data.DataType) - */ @Override public boolean isEquivalent(DataType dataType) { @@ -1148,17 +1302,6 @@ class StructureDB extends CompositeDB implements Structure { return true; } -// private boolean hasRoom(int index, int offset, int length) { -// if (offset+length > this.structLength) { -// return false; -// } -// if (index+1 < components.size()) { -// DataTypeComponent nextDtc = (DataTypeComponent)components.get(index+1); -// return offset+length <= nextDtc.getOffset(); -// } -// return true; -// } - /** * * @param definedComponentIndex the index of the defined component that is consuming the bytes. @@ -1202,9 +1345,7 @@ class StructureDB extends CompositeDB implements Structure { private void shiftOffsets(int definedComponentIndex, int deltaOrdinal, int deltaOffset) { for (int i = definedComponentIndex; i < components.size(); i++) { DataTypeComponentDB dtc = components.get(i); - dtc.setOffset(dtc.getOffset() + deltaOffset, false); - dtc.setOrdinal(dtc.getOrdinal() + deltaOrdinal, false); - dtc.updateRecord(); + shiftOffset(dtc, deltaOrdinal, deltaOffset); } structLength += deltaOffset; if (!isInternallyAligned()) { @@ -1220,41 +1361,15 @@ class StructureDB extends CompositeDB implements Structure { } } - /** - * Replace the component and send a notification according to the - * doNotify param. - */ - private DataTypeComponent doReplace(int offset, DataType dataType, int length, String name, - String comment, boolean doNotify) { - - if (offset < 0) { - throw new IllegalArgumentException("Offset cannot be negative."); - } - validateDataType(dataType); - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } - dataType = resolve(dataType); - checkAncestry(dataType); - - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } - - DataTypeComponent origDtc = getComponentAt(offset); - if (dataType == DataType.DEFAULT) { - int ordinal = origDtc.getOrdinal(); - clearComponent(ordinal); - return getComponent(ordinal); - } - return replaceComponent(origDtc, dataType, length, name, comment, doNotify); + protected void shiftOffset(DataTypeComponentDB dtc, int deltaOrdinal, int deltaOffset) { + dtc.setOffset(dtc.getOffset() + deltaOffset, false); + dtc.setOrdinal(dtc.getOrdinal() + deltaOrdinal, false); + dtc.updateRecord(); } /** * Replace the indicated component with a new component containing the - * specified data type. + * specified data type. Flex-array component not handled. * @param origDtc the original data type component in this structure. * @param resolvedDataType the data type of the new component * @param length the length of the new component @@ -1264,6 +1379,13 @@ class StructureDB extends CompositeDB implements Structure { */ private DataTypeComponent replaceComponent(DataTypeComponent origDtc, DataType resolvedDataType, int length, String name, String comment, boolean doNotify) { + +// FIXME: Unsure how o support replace operation with bit-fields. Within unaligned structure +// the packing behavior for bit-fields prevents a one-for-one replacement and things may shift +// around which the unaligned structure tries to avoid. Insert and delete are less of a concern +// since movement already can occur, although insert at offset may not retain the offset if it +// interacts with bit-fields. + try { int ordinal = origDtc.getOrdinal(); int newOffset = origDtc.getOffset(); @@ -1360,9 +1482,6 @@ class StructureDB extends CompositeDB implements Structure { return getName(); } - /** - * @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType) - */ @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { if (oldDt == this) { @@ -1371,76 +1490,133 @@ class StructureDB extends CompositeDB implements Structure { lock.acquire(); try { checkDeleted(); + DataType replacementDt = newDt; try { - validateDataType(newDt); - if (!(newDt instanceof DataTypeDB) || - (newDt.getDataTypeManager() != getDataTypeManager())) { - newDt = resolve(newDt); + validateDataType(replacementDt); + if (!(replacementDt instanceof DataTypeDB) || + (replacementDt.getDataTypeManager() != getDataTypeManager())) { + replacementDt = resolve(replacementDt); } - checkAncestry(newDt); + checkAncestry(replacementDt); } catch (Exception e) { - newDt = DataType.DEFAULT; + // TODO: should we use Undefined instead to avoid cases where + // DEFAULT datatype can not be used (flex array, bitfield, aligned structure) + // TODO: failing silently is rather hidden + replacementDt = DataType.DEFAULT; } boolean changed = false; - int nextIndex = 0; // index of next defined component. - Iterator it = components.iterator(); - while (it.hasNext()) { - nextIndex++; - DataTypeComponentDB comp = it.next(); - DataType compDt = comp.getDataType(); - if (oldDt == compDt) { - oldDt.removeParent(this); - comp.setDataType(newDt); - newDt.addParent(this); - int len = newDt.getLength(); - int oldLen = comp.getLength(); - if (len > 0) { - if (len < oldLen) { - comp.setLength(len, true); - shiftOffsets(nextIndex, oldLen - len, 0); - } - else if (len > oldLen) { - int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); - int bytesNeeded = len - oldLen; - if (bytesNeeded <= bytesAvailable) { - comp.setLength(len, true); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure - doGrowStructure(bytesNeeded - bytesAvailable); - comp.setLength(len, true); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else { - comp.setLength(oldLen + bytesAvailable, true); - shiftOffsets(nextIndex, -bytesAvailable, 0); - } - } + if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == oldDt) { + flexibleArrayComponent.getDataType().removeParent(this); + if (isInvalidFlexArrayDataType(replacementDt)) { + componentAdapter.removeRecord(flexibleArrayComponent.getKey()); + flexibleArrayComponent = null; + Msg.error(this, "Invalid flex array replacement type " + newDt.getName() + + ", removing flex array: " + getPathName()); + } + else { + flexibleArrayComponent.setDataType(replacementDt); + replacementDt.addParent(this); + } + changed = true; + } + + for (int i = components.size() - 1; i >= 0; i--) { + + DataTypeComponentDB comp = components.get(i); + int nextIndex = i + 1; + + boolean remove = false; + if (comp.isBitFieldComponent()) { + try { + changed |= updateBitFieldDataType(comp, oldDt, replacementDt); } + catch (InvalidDataTypeException e) { + Msg.error(this, + "Invalid bitfield replacement type " + newDt.getName() + + ", removing bitfield " + comp.getDataType().getName() + ": " + + getPathName()); + remove = true; + } + } + else if (comp.getDataType() == oldDt) { + if (replacementDt == DEFAULT && isInternallyAligned()) { + Msg.error(this, + "Invalid replacement type " + newDt.getName() + + ", removing component " + comp.getDataType().getName() + ": " + + getPathName()); + remove = true; + } + else { + setComponentDataType(comp, replacementDt, nextIndex); + changed = true; + } + } + if (remove) { + // error case - remove component + oldDt.removeParent(this); + components.remove(i); + shiftOffsets(i, comp.getLength() - 1, 0); // ordinals only + componentAdapter.removeRecord(comp.getKey()); changed = true; } } if (changed) { + adjustInternalAlignment(false); notifySizeChanged(); } } + catch (IOException e) { + dataMgr.dbError(e); + } finally { lock.release(); } - adjustInternalAlignment(true); } - /** - * @see ghidra.program.model.data.DataType#dataTypeNameChanged(ghidra.program.model.data.DataType, java.lang.String) - */ + private void setComponentDataType(DataTypeComponentDB comp, DataType replacementDt, + int nextIndex) { + + comp.getDataType().removeParent(this); + comp.setDataType(replacementDt); + replacementDt.addParent(this); + + if (isInternallyAligned()) { + return; // caller must invoke adjustInternalAlignment + } + + int len = replacementDt.getLength(); + int oldLen = comp.getLength(); + if (len > 0) { + if (len < oldLen) { + comp.setLength(len, true); + shiftOffsets(nextIndex, oldLen - len, 0); + } + else if (len > oldLen) { + int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); + int bytesNeeded = len - oldLen; + if (bytesNeeded <= bytesAvailable) { + comp.setLength(len, true); + shiftOffsets(nextIndex, -bytesNeeded, 0); + } + else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure + doGrowStructure(bytesNeeded - bytesAvailable); + comp.setLength(len, true); + shiftOffsets(nextIndex, -bytesNeeded, 0); + } + else { + comp.setLength(oldLen + bytesAvailable, true); + shiftOffsets(nextIndex, -bytesAvailable, 0); + } + } + } + } + @Override public void dataTypeNameChanged(DataType dt, String oldName) { + // do nothing } - /* (non-Javadoc) - * @see ghidra.program.model.data.Structure#deleteAll() - */ @Override public void deleteAll() { lock.acquire(); @@ -1448,12 +1624,8 @@ class StructureDB extends CompositeDB implements Structure { checkDeleted(); if (flexibleArrayComponent != null) { - try { - componentAdapter.removeRecord(flexibleArrayComponent.getKey()); - } - catch (IOException e) { - dataMgr.dbError(e); - } + flexibleArrayComponent.getDataType().removeParent(this); + componentAdapter.removeRecord(flexibleArrayComponent.getKey()); flexibleArrayComponent = null; } @@ -1472,60 +1644,22 @@ class StructureDB extends CompositeDB implements Structure { numComponents = 0; record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, 0); record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, 0); - try { - compositeAdapter.updateRecord(record, true); - } - catch (IOException e) { - dataMgr.dbError(e); - } + compositeAdapter.updateRecord(record, true); adjustInternalAlignment(true); notifySizeChanged(); } + catch (IOException e) { + dataMgr.dbError(e); + } finally { lock.release(); } } - private static class OffsetComparator implements Comparator { - - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - @Override - public int compare(Object o1, Object o2) { - if (o1 instanceof Integer) { - return -compare(o2, o1); - } - DataTypeComponent dtc = (DataTypeComponent) o1; - int offset = ((Integer) o2).intValue(); - if (offset < dtc.getOffset()) { - return 1; - } - else if (offset > dtc.getEndOffset()) { - return -1; - } - return 0; - } - - } - - private static class OrdinalComparator implements Comparator { - - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - @Override - public int compare(Object o1, Object o2) { - if (o1 instanceof Integer) { - return -compare(o2, o1); - } - DataTypeComponent dtc = (DataTypeComponent) o1; - int ordinal = ((Integer) o2).intValue(); - return dtc.getOrdinal() - ordinal; - } - - } - + /** + * ComponentComparator provides ability to compare two DataTypeComponent objects + * based upon their ordinal. Intended to be used to sort components based upon ordinal. + */ private static class ComponentComparator implements Comparator { @Override public int compare(DataTypeComponent dtc1, DataTypeComponent dtc2) { @@ -1546,90 +1680,40 @@ class StructureDB extends CompositeDB implements Structure { * @return true if the structure was changed by this method. */ private boolean adjustComponents(boolean notify) { - boolean internallyAligned = isInternallyAligned(); - boolean keepDefinedDefaults = !internallyAligned; lock.acquire(); try { checkDeleted(); - int oldLength = structLength; + + boolean changed = false; + alignment = -1; if (!isInternallyAligned()) { - boolean changed = adjustUnalignedComponents(); + changed |= adjustUnalignedComponents(); if (notify && changed) { dataMgr.dataTypeChanged(this); } return changed; } - boolean compositeDBChanged = false; - boolean componentsDBChanged = false; - int packingAlignment = getPackingValue(); + int oldLength = structLength; - // Adjust each of the components. - int currentOrdinal = 0; - int currentOffset = 0; - int allComponentsLCM = 1; - for (DataTypeComponentDB dataTypeComponent : components) { - - DataType componentDt = dataTypeComponent.getDataType(); - if (!keepDefinedDefaults && DataType.DEFAULT == componentDt) { - continue; // Discard a defined Default data type. - } - int componentLength = dataTypeComponent.getLength(); - int componentOrdinal = dataTypeComponent.getOrdinal(); - int componentOffset = dataTypeComponent.getOffset(); - int dtLength = componentDt.getLength(); - if (dtLength <= 0) { - dtLength = componentLength; - } - - int componentAlignment = - getPackedAlignment(componentDt, dtLength, packingAlignment); - - allComponentsLCM = DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, - componentAlignment); - - int newOffset = DataOrganizationImpl.getOffset(componentAlignment, currentOffset); - currentOffset = newOffset + dtLength; - if (componentOrdinal == currentOrdinal && componentOffset == newOffset && - componentLength == dtLength) { - currentOrdinal++; - continue; // No change needed. - } - dataTypeComponent.setOffset(newOffset, false); - dataTypeComponent.setOrdinal(currentOrdinal, false); - dataTypeComponent.setLength(dtLength, false); - dataTypeComponent.updateRecord(); - currentOrdinal++; - componentsDBChanged = true; - } - - if (flexibleArrayComponent != null) { - // account for flexible array type in any end of structure padding - DataType dataType = flexibleArrayComponent.getDataType(); - int componentAlignment = - getPackedAlignment(dataType, dataType.getLength(), packingAlignment); - currentOffset = DataOrganizationImpl.getOffset(componentAlignment, currentOffset); - allComponentsLCM = DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, - componentAlignment); - } + StructurePackResult packResult = + AlignedStructurePacker.packComponents(this, components); + changed = packResult.componentsChanged; // Adjust the structure - compositeDBChanged = updateComposite(currentOrdinal, currentOffset, false); + changed |= updateComposite(packResult.numComponents, packResult.structureLength, + packResult.alignment, false); - boolean addedPadding = alignEndOfStruct(allComponentsLCM); - - if (notify) { - if (componentsDBChanged || compositeDBChanged || addedPadding) { - if (oldLength != structLength) { - notifySizeChanged(); - } - else { - dataMgr.dataTypeChanged(this); - } - return true; + if (notify & changed) { + if (oldLength != structLength) { + notifySizeChanged(); } + else { + dataMgr.dataTypeChanged(this); + } + return true; } return false; } @@ -1638,45 +1722,23 @@ class StructureDB extends CompositeDB implements Structure { } } - private int getPackedAlignment(DataType componentDt, int dtLength, int packingAlignment) { - DataOrganization dataOrganization = getDataOrganization(); - int componentAlignment = dataOrganization.getAlignment(componentDt, dtLength); - int componentForcedAlignment = dataOrganization.getForcedAlignment(componentDt); - boolean componentForcingAlignment = componentForcedAlignment > 0; - if (componentForcingAlignment) { - componentAlignment = DataOrganizationImpl.getLeastCommonMultiple(componentAlignment, - componentForcedAlignment); - } - if (packingAlignment > 0) { - if (componentForcedAlignment > packingAlignment) { - componentAlignment = componentForcedAlignment; - } - else if (componentAlignment > packingAlignment) { - componentAlignment = packingAlignment; - } - } - return componentAlignment; - } - private boolean adjustUnalignedComponents() { boolean changed = false; - int currentOrdinal = 0; int componentCount = 0; int currentOffset = 0; for (DataTypeComponentDB dataTypeComponent : components) { int componentLength = dataTypeComponent.getLength(); int componentOffset = dataTypeComponent.getOffset(); int numUndefinedsBefore = componentOffset - currentOffset; - componentCount += numUndefinedsBefore; - currentOffset += numUndefinedsBefore; - currentOrdinal += numUndefinedsBefore; - componentCount++; - currentOffset += componentLength; - if (dataTypeComponent.getOrdinal() != currentOrdinal) { - dataTypeComponent.setOrdinal(currentOrdinal, true); + if (numUndefinedsBefore > 0) { + componentCount += numUndefinedsBefore; + } + currentOffset = componentOffset + componentLength; + if (dataTypeComponent.getOrdinal() != componentCount) { + dataTypeComponent.setOrdinal(componentCount, true); changed = true; } - currentOrdinal++; + componentCount++; } int numUndefinedsAfter = structLength - currentOffset; componentCount += numUndefinedsAfter; @@ -1706,7 +1768,7 @@ class StructureDB extends CompositeDB implements Structure { } private boolean updateComposite(int currentNumComponents, int currentLength, - boolean setLastChangeTime) { + int currentAlignment, boolean setLastChangeTime) { boolean compositeChanged = false; if (numComponents != currentNumComponents) { numComponents = currentNumComponents; @@ -1718,6 +1780,10 @@ class StructureDB extends CompositeDB implements Structure { record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); compositeChanged = true; } + if (alignment != currentAlignment) { + alignment = currentAlignment; + compositeChanged = true; + } if (compositeChanged) { try { compositeAdapter.updateRecord(record, setLastChangeTime); @@ -1730,25 +1796,6 @@ class StructureDB extends CompositeDB implements Structure { return false; } - private boolean alignEndOfStruct(int componentLCM) { - int minimumAlignment = getMinimumAlignment(); - int structureLength = getLength(); - if (structureLength == 0) { - return true; - } - int overallAlignment = componentLCM; - if (minimumAlignment > overallAlignment) { - // TODO Should this actually get the LeastCommonMultiple of minimumAlignment and overallAlignment? - overallAlignment = minimumAlignment; - } - int padSize = DataOrganizationImpl.getPaddingSize(overallAlignment, structLength); // FIXME Fix cast. - if (padSize > 0) { - doGrowStructure(padSize); - return true; - } - return false; - } - @Override public void realign() { if (isInternallyAligned()) { @@ -1776,10 +1823,20 @@ class StructureDB extends CompositeDB implements Structure { return flexibleArrayComponent; } + private boolean isInvalidFlexArrayDataType(DataType dataType) { + return (dataType == null || dataType == DataType.DEFAULT || + dataType instanceof BitFieldDataType || dataType instanceof Dynamic || + dataType instanceof FactoryDataType); + } + @Override public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, String comment) { - return doAdd(flexType, 0, name, comment, true, true); + if (isInvalidFlexArrayDataType(flexType)) { + throw new IllegalArgumentException( + "Unsupported flexType: " + flexType.getDisplayName()); + } + return doAdd(flexType, 0, true, name, comment, true); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java index acaa152fc6..8b1158c6d3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java @@ -23,6 +23,7 @@ import ghidra.docking.settings.Settings; import ghidra.program.database.DBObjectCache; import ghidra.program.model.data.*; import ghidra.program.model.mem.MemBuffer; +import ghidra.util.Msg; /** * Database implementation for the Union data type. @@ -69,9 +70,6 @@ class UnionDB extends CompositeDB implements Union { } - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.util.settings.Settings, int) - */ @Override public String getRepresentation(MemBuffer buf, Settings settings, int length) { if (isNotYetDefined()) { @@ -85,24 +83,13 @@ class UnionDB extends CompositeDB implements Union { return components.size() == 0; } - /** - * @see ghidra.program.model.data.EditableComposite#add(ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent add(DataType dataType, int length, String name, String comment) { lock.acquire(); try { checkDeleted(); - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } - validateDataType(dataType); - dataType = resolve(dataType); - checkAncestry(dataType); - int oldLength = unionLength; DataTypeComponent dtc = doAdd(dataType, length, name, comment); - adjustInternalAlignment(false); - updateLength(oldLength, true, true); + adjustLength(true, true); return dtc; } finally { @@ -110,21 +97,41 @@ class UnionDB extends CompositeDB implements Union { } } - private DataTypeComponent doAdd(DataType resolvedDataType, int length, String name, - String comment) { + private int getBitFieldAllocation(BitFieldDataType bitfieldDt) { - // TODO Is this the right place to adjust the length? - int dtLength = resolvedDataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; + BitFieldPacking bitFieldPacking = getBitFieldPacking(); + if (bitFieldPacking.useMSConvention()) { + return bitfieldDt.getBaseTypeSize(); } - DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(resolvedDataType), length, + if (bitfieldDt.getBitSize() == 0) { + return 0; + } + + int length = bitfieldDt.getBaseTypeSize(); + int packValue = getPackingValue(); + if (packValue != NOT_PACKING && length > packValue) { + length = + DataOrganizationImpl.getLeastCommonMultiple(bitfieldDt.getStorageSize(), packValue); + } + return length; + } + + private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment) { + + validateDataType(dataType); + + dataType = resolve(dataType); + checkAncestry(dataType); + + length = getPreferredComponentLength(dataType, length); + + DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(dataType), length, components.size(), 0, name, comment); - resolvedDataType.addParent(this); + dataType.addParent(this); components.add(dtc); - unionLength = Math.max(unionLength, length); + return dtc; } @@ -150,37 +157,26 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.EditableComposite#insert(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name, String comment) { lock.acquire(); try { checkDeleted(); - if (length < 1) { - throw new IllegalArgumentException("Minimum component length is 1 byte"); - } validateDataType(dataType); + dataType = resolve(dataType); checkAncestry(dataType); - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } + length = getPreferredComponentLength(dataType, length); - int oldLength = unionLength; DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(dataType), length, ordinal, 0, name, comment); dataType.addParent(this); shiftOrdinals(ordinal, 1); components.add(ordinal, dtc); -// unionLength = Math.max(unionLength, length); - adjustInternalAlignment(true); - updateLength(oldLength, true, true); + + adjustLength(true, true); return dtc; } finally { @@ -188,22 +184,68 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.EditableComposite#delete(int) - */ + @Override + public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { + + if (ordinal < 0 || ordinal > components.size()) { + throw new ArrayIndexOutOfBoundsException(ordinal); + } + + if (isInternallyAligned()) { + BitFieldDataType bitFieldDt = + new BitFieldDBDataType(baseDataType, bitSize, 0, 0, dataMgr); + return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); + } + + if (byteWidth <= 0) { + throw new IllegalArgumentException("Invalid byteWidth"); + } + + // handle unaligned case - use minimal storage + // bitfield value will be forced based upon byteWidth, bitSize and endianess + boolean bigEndian = getDataOrganization().isBigEndian(); + int effectiveBitSize = + BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength()); + int storageSize = BitFieldDataType.getMinimumStorageSize(effectiveBitSize); + if (byteWidth < storageSize) { + throw new IllegalArgumentException( + "Bitfield does not fit within specified constraints"); + } + int storageBitOffset = 0; + if (bigEndian) { + storageBitOffset = (8 * storageSize) - effectiveBitSize; + } + + BitFieldDataType bitfieldDt = new BitFieldDBDataType(baseDataType, bitSize, + storageBitOffset, storageSize, getDataTypeManager()); + + DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(bitfieldDt), storageSize, + ordinal, 0, componentName, comment); + + bitfieldDt.addParent(this); // currently has no affect + + shiftOrdinals(ordinal, 1); + components.add(ordinal, dtc); + + adjustLength(true, true); + + return dtc; + } + @Override public void delete(int ordinal) { lock.acquire(); try { checkDeleted(); - int oldLength = unionLength; + DataTypeComponentDB dtc = components.remove(ordinal); dtc.getDataType().removeParent(this); removeComponent(dtc.getKey()); shiftOrdinals(ordinal, -1); -// unionLength = computeUnpaddedUnionLength(); - adjustInternalAlignment(false); - updateLength(oldLength, true, true); + + adjustLength(true, true); } finally { lock.release(); @@ -232,18 +274,14 @@ class UnionDB extends CompositeDB implements Union { if (!(dataType instanceof Union)) { throw new IllegalArgumentException(); } - doReplaceWith((Union) dataType, true); - } - - void doReplaceWith(Union union, boolean notify) { - doReplaceWith(union, notify, null); + doReplaceWith((Union) dataType, true, null); } void doReplaceWith(Union union, boolean notify, DataTypeConflictHandler handler) { lock.acquire(); try { checkDeleted(); - int oldLength = unionLength; + long oldMinAlignment = getMinimumAlignment(); for (int i = 0; i < components.size(); i++) { DataTypeComponentDB dtc = components.get(i); @@ -252,21 +290,15 @@ class UnionDB extends CompositeDB implements Union { } components.clear(); - DataTypeComponent[] otherComponents = union.getComponents(); - for (int i = 0; i < otherComponents.length; i++) { - DataTypeComponent dtc = otherComponents[i]; - DataType dt = dtc.getDataType(); - dt = resolve(dt, handler); - checkAncestry(dt); - int dtLength = dt.getLength(); - if (dtLength <= 0) { - dtLength = dtc.getLength(); - } - doAdd(dt, dtLength, dtc.getFieldName(), dtc.getComment()); - } - setDescription(union.getDescription()); setAlignment(union, notify); - updateLength(oldLength, notify, true); + + for (DataTypeComponent dtc : union.getComponents()) { + DataType dt = dtc.getDataType(); + doAdd(dt, dtc.getLength(), dtc.getFieldName(), dtc.getComment()); + } + + adjustLength(notify, true); // TODO: VERIFY! is it always appropriate to set update time?? + if (notify && (oldMinAlignment != getMinimumAlignment())) { notifyAlignmentChanged(); } @@ -277,31 +309,6 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.EditableComposite#contains(ghidra.program.model.data.DataType) - */ - public boolean contains(DataType dataType) { - lock.acquire(); - try { - checkIsValid(); - for (int i = 0; i < components.size(); i++) { - DataTypeComponent dtc = components.get(i); - DataType dt = dtc.getDataType(); - - if (dt == dataType) { - return true; - } - } - return false; - } - finally { - lock.release(); - } - } - - /** - * @see ghidra.program.model.data.EditableComposite#isPartOf(ghidra.program.model.data.DataType) - */ @Override public boolean isPartOf(DataType dataType) { lock.acquire(); @@ -310,8 +317,7 @@ class UnionDB extends CompositeDB implements Union { if (equals(dataType)) { return true; } - for (int i = 0; i < components.size(); i++) { - DataTypeComponent dtc = components.get(i); + for (DataTypeComponent dtc : components) { DataType subDt = dtc.getDataType(); if (subDt instanceof Composite) { if (((Composite) subDt).isPartOf(dataType)) { @@ -329,17 +335,11 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.Composite#getNumComponents(ghidra.program.model.mem.MemBuffer) - */ @Override public int getNumComponents() { lock.acquire(); try { checkIsValid(); -// if (components.size() == 0) { -// return 1; // lie -// } return components.size(); } finally { @@ -347,9 +347,6 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.Composite#getComponent(int, ghidra.program.model.mem.MemBuffer) - */ @Override public DataTypeComponent getComponent(int ordinal) { lock.acquire(); @@ -365,9 +362,6 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.Composite#getComponents(ghidra.program.model.mem.MemBuffer) - */ @Override public DataTypeComponent[] getComponents() { lock.acquire(); @@ -397,9 +391,6 @@ class UnionDB extends CompositeDB implements Union { return union; } - /** - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { lock.acquire(); @@ -415,30 +406,22 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.DataType#dataTypeSizeChanged(ghidra.program.model.data.DataType) - */ @Override public void dataTypeSizeChanged(DataType dt) { lock.acquire(); try { checkDeleted(); boolean changed = false; - int oldLength = unionLength; - unionLength = 0; - for (int i = 0; i < components.size(); i++) { - DataTypeComponentDB dtc = components.get(i); - DataType tmpDt = dtc.getDataType(); - int tmpLen = tmpDt.getLength(); - if ((tmpDt.isEquivalent(dt)) && (tmpLen > 0) && (tmpLen != dtc.getLength())) { - dtc.setLength(tmpLen, true); + for (DataTypeComponentDB dtc : components) { + int length = dtc.getLength(); + if (dtc.getDataType() == dt) { + length = getPreferredComponentLength(dt, length); + dtc.setLength(length, true); changed = true; } - unionLength = Math.max(unionLength, dtc.getLength()); } if (changed) { - adjustInternalAlignment(false); - updateLength(oldLength, true, false); + adjustLength(true, false); } } finally { @@ -451,51 +434,61 @@ class UnionDB extends CompositeDB implements Union { adjustInternalAlignment(true); } - public void adjustLength() { - adjustLength(true); - } - - public void adjustLength(boolean notify) { + private void adjustLength(boolean notify, boolean setLastChangeTime) { lock.acquire(); try { checkDeleted(); int oldLength = unionLength; - unionLength = getLength(getDataOrganization(), isInternallyAligned()); - updateLength(oldLength, notify, false); + + unionLength = 0; + for (DataTypeComponent dtc : components) { + + int length = dtc.getLength(); + if (isInternallyAligned() && dtc.isBitFieldComponent()) { + // revise length to reflect compiler bitfield allocation rules + length = getBitFieldAllocation((BitFieldDataType) dtc.getDataType()); + } + + unionLength = Math.max(length, unionLength); + } + + DataOrganization dataOrganization = getDataOrganization(); + int alignment = dataOrganization.getAlignment(this, unionLength); + int amountFilled = unionLength % alignment; + if (amountFilled > 0) { + unionLength += alignment - amountFilled; + } + + updateLength(oldLength, notify, setLastChangeTime); } finally { lock.release(); } } - /** - * @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType) - */ @Override public void dataTypeDeleted(DataType dt) { lock.acquire(); try { checkDeleted(); - int oldLength = unionLength; - boolean didDelete = false; - for (int i = components.size() - 1; i >= 0; i--) { + boolean didChange = false; + for (int i = components.size() - 1; i >= 0; i--) { // reverse order DataTypeComponentDB dtc = components.get(i); - if (dtc.getDataType() == dt) { + boolean removeBitFieldComponent = false; + if (dtc.isBitFieldComponent()) { + BitFieldDataType bitfieldDt = (BitFieldDataType) dtc.getDataType(); + removeBitFieldComponent = bitfieldDt.getBaseDataType() == dt; + } + if (removeBitFieldComponent || dtc.getDataType() == dt) { dt.removeParent(this); components.remove(i); removeComponent(dtc.getKey()); shiftOrdinals(i, -1); - didDelete = true; + didChange = true; } } - if (didDelete) { - adjustInternalAlignment(false); - if (unionLength == 0) { - dataMgr.addDataTypeToDelete(key); - } - else { - updateLength(oldLength, true, false); - } + if (didChange) { + adjustLength(true, true); } } finally { @@ -503,10 +496,6 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * - * @see ghidra.program.model.data.DataType#isEquivalent(ghidra.program.model.data.DataType) - */ @Override public boolean isEquivalent(DataType dt) { if (dt == this) { @@ -549,32 +538,19 @@ class UnionDB extends CompositeDB implements Union { private void updateLength(int oldLength, boolean notify, boolean setLastChangeTime) { if (oldLength != unionLength) { record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, unionLength); -// record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, -// (new Date()).getTime()); try { compositeAdapter.updateRecord(record, setLastChangeTime); } catch (IOException e) { dataMgr.dbError(e); } -// if (notify) { notifySizeChanged(); -// } } else if (notify) { dataMgr.dataTypeChanged(this); } } - private int computeUnpaddedUnionLength() { - int unpaddedLength = 0; - for (int i = 0; i < components.size(); i++) { - unpaddedLength = - Math.max(unpaddedLength, ((DataTypeComponent) components.get(i)).getLength()); - } - return unpaddedLength; - } - private void shiftOrdinals(int ordinal, int deltaOrdinal) { for (int i = ordinal; i < components.size(); i++) { DataTypeComponentDB dtc = components.get(i); @@ -582,9 +558,6 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType) - */ @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { if (oldDt == this) { @@ -593,39 +566,67 @@ class UnionDB extends CompositeDB implements Union { lock.acquire(); try { checkDeleted(); + DataType replacementDt = newDt; try { - validateDataType(newDt); - if (!(newDt instanceof DataTypeDB) || - (newDt.getDataTypeManager() != getDataTypeManager())) { - newDt = resolve(newDt); + validateDataType(replacementDt); + if (!(replacementDt instanceof DataTypeDB) || + (replacementDt.getDataTypeManager() != getDataTypeManager())) { + replacementDt = resolve(replacementDt); } - checkAncestry(newDt); + checkAncestry(replacementDt); } catch (Exception e) { - newDt = new ByteDataType(); + // TODO: should we use Undefined instead since we do not support + // DEFAULT in Unions + replacementDt = DataType.DEFAULT; } boolean changed = false; - int oldLength = getLength(); - Iterator it = components.iterator(); - while (it.hasNext()) { - DataTypeComponentDB comp = it.next(); - DataType compDt = comp.getDataType(); - if (oldDt == compDt) { - oldDt.removeParent(this); - comp.setDataType(newDt); - newDt.addParent(this); - int len = newDt.getLength(); - if (len > 0) { - comp.setLength(len, true); + for (int i = components.size() - 1; i >= 0; i--) { + + DataTypeComponentDB dtc = components.get(i); + + boolean remove = false; + if (dtc.isBitFieldComponent()) { + try { + changed |= updateBitFieldDataType(dtc, oldDt, replacementDt); } + catch (InvalidDataTypeException e) { + Msg.error(this, + "Invalid bitfield replacement type " + newDt.getName() + + ", removing bitfield " + dtc.getDataType().getName() + ": " + + getPathName()); + remove = true; + } + } + else if (dtc.getDataType() == oldDt) { + if (replacementDt == DEFAULT) { + Msg.error(this, + "Invalid replacement type " + newDt.getName() + + ", removing component " + dtc.getDataType().getName() + ": " + + getPathName()); + remove = true; + } + else { + oldDt.removeParent(this); + dtc.setDataType(replacementDt); + replacementDt.addParent(this); + int len = replacementDt.getLength(); + if (len > 0) { + dtc.setLength(len, true); + } + changed = true; + } + } + if (remove) { + oldDt.removeParent(this); + components.remove(i); + removeComponent(dtc.getKey()); + shiftOrdinals(i, -1); changed = true; } } if (changed) { - adjustInternalAlignment(false); - if (oldLength != getLength()) { - updateLength(oldLength, true, true); - } + adjustLength(true, true); } } finally { @@ -633,16 +634,11 @@ class UnionDB extends CompositeDB implements Union { } } - /** - * @see ghidra.program.model.data.DataType#dataTypeNameChanged(ghidra.program.model.data.DataType, java.lang.String) - */ @Override public void dataTypeNameChanged(DataType dt, String oldName) { + // ignored } - /** - * @see ghidra.program.model.data.DataType#dependsOn(ghidra.program.model.data.DataType) - */ @Override public boolean dependsOn(DataType dt) { lock.acquire(); @@ -671,33 +667,6 @@ class UnionDB extends CompositeDB implements Union { return "UNION_" + getName(); } - public int getLength(DataOrganization dataOrganization, boolean padEnd) { - int unpaddedLength = computeUnpaddedUnionLength(); - int newLength = unpaddedLength; - if (padEnd) { - newLength += getPaddingSize(dataOrganization, unpaddedLength); - } - return newLength; - } - - private int getPaddingSize(DataOrganization dataOrganization, int unpaddedLength) { - int alignment = dataOrganization.getAlignment(this, unpaddedLength); - int amountFilled = unpaddedLength % alignment; - if (amountFilled > 0) { - return alignment - amountFilled; - } - return 0; - } - - /* (non-Javadoc) - * @see ghidra.program.model.data.Composite#setAligned(boolean) - */ - @Override - public void setInternallyAligned(boolean aligned) { - super.setInternallyAligned(aligned); - adjustInternalAlignment(true); - } - @Override public void realign() { if (isInternallyAligned()) { @@ -707,6 +676,6 @@ class UnionDB extends CompositeDB implements Union { @Override public void adjustInternalAlignment(boolean notify) { - adjustLength(notify); + adjustLength(notify, false); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionManager.java index 2e59d670fe..a9ba121866 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionManager.java @@ -82,7 +82,7 @@ public class OldFunctionManager implements ErrorHandler { throw new AssertException("Function manager already upgraded"); } this.program = upgradeProgram; - dataManager = upgradeProgram.getDataManager(); + dataManager = upgradeProgram.getDataTypeManager(); monitor.setMessage("Upgrading Functions..."); monitor.initialize(getFunctionCount()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDB.java index 6a16261206..4b697254ed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDB.java @@ -151,7 +151,7 @@ public class EquateDB extends DatabaseObject implements Equate { public String getDisplayName() { String equateName = getName(); if (isEnumBased()) { - DataTypeManager dtm = equateMgr.getProgram().getDataManager(); + DataTypeManager dtm = equateMgr.getProgram().getDataTypeManager(); UniversalID id = EquateManager.getDataTypeUUID(equateName); Enum enoom = (Enum) dtm.findDataTypeForID(id); if (enoom == null || enoom.getName(getValue()) == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java index 20e93c48a6..ddedc6047f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java @@ -197,10 +197,10 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { return; } - if (program.getDataManager().findDataTypeForID(enoom.getUniversalID()) == null) { + if (program.getDataTypeManager().findDataTypeForID(enoom.getUniversalID()) == null) { int transactionID = program.startTransaction("Set Equate Dialog"); try { - enoom = (Enum) program.getDataManager().addDataType(enoom, null); + enoom = (Enum) program.getDataTypeManager().addDataType(enoom, null); } finally { program.endTransaction(transactionID, true); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java new file mode 100644 index 0000000000..64f2d9f6bd --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java @@ -0,0 +1,257 @@ +/* ### + * 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.program.model.data; + +import java.net.URL; + +import ghidra.app.plugin.core.datamgr.archive.SourceArchive; +import ghidra.docking.settings.Settings; +import ghidra.program.model.mem.MemBuffer; +import ghidra.util.InvalidNameException; +import ghidra.util.UniversalID; +import ghidra.util.exception.DuplicateNameException; + +/** + * Base class for DataType classes. Many of the DataType methods are stubbed out so simple datatype + * classes can be created without implementing too many methods. + */ +public abstract class AbstractDataType implements DataType { + protected String name; + protected CategoryPath categoryPath; + protected final DataTypeManager dataMgr; + private DataOrganization dataOrganization; + + protected AbstractDataType(CategoryPath path, String name, DataTypeManager dataTypeManager) { + if (path == null) { + throw new IllegalArgumentException("Category Path is null!"); + } + if (name == null || name.length() == 0) { + throw new IllegalArgumentException("Name is null or empty!"); + } + // allow spaces since derived types may have spaces (pointers for example: foo *32) + if (!DataUtilities.isValidDataTypeName(name)) { + throw new IllegalArgumentException("Invalid DataType name: " + name); + } + + this.categoryPath = path; + this.name = name; + this.dataMgr = dataTypeManager; + } + + @Override + public CategoryPath getCategoryPath() { + return categoryPath; + } + + /** + * @see ghidra.program.model.data.DataType#getDataTypeManager() + */ + @Override + public final DataTypeManager getDataTypeManager() { + return dataMgr; + } + + @Override + public final DataOrganization getDataOrganization() { + if (dataOrganization != null) { + return dataOrganization; + } + if (dataMgr != null) { + dataOrganization = dataMgr.getDataOrganization(); + } + if (dataOrganization == null) { + dataOrganization = DataOrganizationImpl.getDefaultOrganization(); + } + return dataOrganization; + } + + @Override + public DataTypePath getDataTypePath() { + return new DataTypePath(categoryPath, name); + } + + @Override + public URL getDocs() { + return null; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getPathName() { + return getDataTypePath().getPath(); + } + + @Override + public String getDisplayName() { + return getName(); + } + + @Override + public String getMnemonic(Settings settings) { + return name; + } + + @Override + public boolean isNotYetDefined() { + return false; + } + + @Override + public String toString() { + return getDisplayName(); + } + + @Override + public boolean isDeleted() { + return false; + } + + @Override + public void setName(String name) throws InvalidNameException { + // default is immutable + } + + @Override + public void setNameAndCategory(CategoryPath path, String name) + throws InvalidNameException, DuplicateNameException { + // default is immutable + } + + @Override + public void dataTypeSizeChanged(DataType dt) { + // do nothing + } + + @Override + public void dataTypeDeleted(DataType dt) { + // do nothing + } + + @Override + public void dataTypeReplaced(DataType oldDt, DataType newDt) { + // do nothing + } + + @Override + public void addParent(DataType dt) { + // not-applicable + } + + @Override + public void removeParent(DataType dt) { + // not-applicable + } + + @Override + public DataType[] getParents() { + // not-applicable + return null; + } + + @Override + public boolean dependsOn(DataType dt) { + return false; + } + + @Override + public SourceArchive getSourceArchive() { + return null; // do nothing + } + + @Override + public void setSourceArchive(SourceArchive archive) { + // do nothing + } + + @Override + public long getLastChangeTime() { + // do nothing + return 0; + } + + @Override + public long getLastChangeTimeInSourceArchive() { + // do nothing + return 0; + } + + @Override + public UniversalID getUniversalID() { + return null; + } + + @Override + public void dataTypeNameChanged(DataType dt, String oldName) { + // do nothing + } + + @Override + public void replaceWith(DataType dataType) { + // do nothing + } + + @Override + public void setLastChangeTime(long lastChangeTime) { + // do nothing + } + + @Override + public void setLastChangeTimeInSourceArchive(long lastChangeTimeInSourceArchive) { + // do nothing + } + + @Override + public void setDescription(String description) throws UnsupportedOperationException { + // immutable + } + + @Override + public boolean isDynamicallySized() { + return false; // not applicable + } + + @Override + public String getDefaultLabelPrefix() { + return null; + } + + @Override + public String getDefaultAbbreviatedLabelPrefix() { + return getDefaultLabelPrefix(); + } + + @Override + public void setCategoryPath(CategoryPath path) throws DuplicateNameException { + // not-applicable + } + + @Override + public String getDefaultLabelPrefix(MemBuffer buf, Settings settings, int len, + DataTypeDisplayOptions options) { + return getDefaultLabelPrefix(); + } + + @Override + public String getDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings, int len, + DataTypeDisplayOptions options, int offcutLength) { + // By default we will do nothing different for offcut values + return getDefaultLabelPrefix(buf, settings, len, options); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java index 815de73af5..81a4dbea68 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java @@ -18,6 +18,7 @@ package ghidra.program.model.data; import java.math.BigInteger; import ghidra.docking.settings.*; +import ghidra.program.model.mem.ByteMemBufferImpl; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.scalar.Scalar; import ghidra.util.StringFormat; @@ -162,9 +163,27 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt } /** - * - * @see ghidra.program.model.data.DataType#getValue(ghidra.program.model.mem.MemBuffer, ghidra.docking.settings.Settings, int) + * Get the value of integer data as a BigInteger + * @param buf the data buffer. + * @param settings the settings to use. + * @return BigInteger data value */ + public BigInteger getBigIntegerValue(MemBuffer buf, Settings settings) { + Object value = getValue(buf, settings, getLength()); + if (value instanceof Scalar) { + Scalar s = (Scalar) value; + return s.getBigInteger(); + } + if (value instanceof BigInteger) { + return (BigInteger) value; + } + if (value instanceof Character) { + // FIXME: consider flipping around getValue and getBigIntegerValue + return BigInteger.valueOf((Character) value); + } + return null; + } + @Override public Object getValue(MemBuffer buf, Settings settings, int length) { @@ -233,8 +252,6 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt return "??"; } - int format = getFormatSettingsDefinition().getFormat(settings); - boolean padded = PADDING.isPadded(settings); boolean isBigEndian = ENDIAN.isBigEndian(settings, buf); if (!isBigEndian) { @@ -245,78 +262,72 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt bytes = flipped; } + return getRepresentation(new BigInteger(bytes), settings, 8 * length); + } + + /** + * Get integer representation of the big-endian value. + * @param bigInt BigInteger value with the appropriate sign + * @param settings integer format settings (PADDING, FORMAT, etc.) + * @return formatted integer string + */ + public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength) { + + int format = getFormatSettingsDefinition().getFormat(settings); + boolean padded = PADDING.isPadded(settings); + + boolean negative = bigInt.signum() < 0; + + if (negative && (!signed || (format != FormatSettingsDefinition.DECIMAL))) { + // force use of unsigned value + bigInt = bigInt.add(BigInteger.valueOf(2).pow(bitLength)); + } + + int nominalLen; + if (format == FormatSettingsDefinition.CHAR) { int charSize = Math.min(getDataOrganization().getCharSize(), getLength()); - return new StringDataInstance(this, settings, buf, charSize).getCharRepresentation(); + nominalLen = (bitLength + 7) / 8; + byte[] bytes = bigInt.toByteArray(); + if (bytes.length > nominalLen) { + // BigInteger supplied too many bytes + byte[] chars = new byte[nominalLen]; + System.arraycopy(bytes, bytes.length - nominalLen, chars, 0, nominalLen); + bytes = chars; + } + else if (bytes.length < nominalLen) { + // BigInteger supplied too few bytes + byte[] chars = new byte[nominalLen]; + System.arraycopy(bytes, 0, chars, nominalLen - bytes.length, bytes.length); + bytes = chars; + } + MemBuffer memBuf = new ByteMemBufferImpl(null, bytes, true); + return new StringDataInstance(this, settings, memBuf, charSize).getCharRepresentation(); } - boolean negative = (bytes[0] < 0); // check most-significant-byte sign - int nominalLen; String valStr; - if (size >= 8) { - // Use BigInteger - too big for long - if ((negative && !signed) || (format != FormatSettingsDefinition.DECIMAL)) { - byte[] unsignedBytes = new byte[size + 1]; - unsignedBytes[size] = 0; - System.arraycopy(bytes, 0, unsignedBytes, 1, size); - bytes = unsignedBytes; - } - BigInteger bigInt = new BigInteger(bytes); - switch (format) { - default: - case FormatSettingsDefinition.HEX: - valStr = bigInt.toString(16).toUpperCase() + "h"; - nominalLen = (2 * size) + 1; - break; - case FormatSettingsDefinition.DECIMAL: - String sign = ""; - if (negative && signed) { - sign = "-"; - bigInt = bigInt.negate(); - } - return sign + bigInt.toString(10); - case FormatSettingsDefinition.BINARY: - valStr = bigInt.toString(2) + "b"; - nominalLen = (8 * size) + 1; - break; - case FormatSettingsDefinition.OCTAL: - valStr = bigInt.toString(8) + "o"; - nominalLen = (3 * size) + 1; - break; - } - } - else { - // Use long when possible - long val = 0; - for (byte b : bytes) { - val = (val << 8) + (b & 0x0ffL); - } - switch (format) { - default: - case FormatSettingsDefinition.HEX: - valStr = Long.toString(val, 16).toUpperCase() + 'h'; - nominalLen = (2 * size) + 1; - break; - case FormatSettingsDefinition.DECIMAL: - String sign = ""; - if (negative && signed) { - sign = "-"; - val = (~val + 1) & ~-(1L << (8 * size)); - } - return sign + Long.toString(val); - case FormatSettingsDefinition.BINARY: - valStr = Long.toString(val, 2) + 'b'; - nominalLen = (8 * size) + 1; - break; - case FormatSettingsDefinition.OCTAL: - valStr = Long.toString(val, 8) + 'o'; - nominalLen = (3 * size) + 1; - break; - } + switch (format) { + default: + case FormatSettingsDefinition.HEX: + valStr = bigInt.toString(16).toUpperCase() + "h"; + nominalLen = (bitLength + 3) / 4; + break; + case FormatSettingsDefinition.DECIMAL: + return bigInt.toString(10); + case FormatSettingsDefinition.BINARY: + valStr = bigInt.toString(2) + "b"; + nominalLen = bitLength; + break; + case FormatSettingsDefinition.OCTAL: + valStr = bigInt.toString(8) + "o"; + nominalLen = (bitLength + 2) / 3; + break; } + if (padded) { - valStr = StringFormat.padIt(valStr, nominalLen, (char) 0, true); + // +1 to account for format suffix char + valStr = StringFormat.padIt(valStr, nominalLen + 1, (char) 0, true); } return valStr; } @@ -356,7 +367,7 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt * this data-type. For example, this method on IntegerDataType will * return an instance of UnsignedIntegerDataType. */ - public abstract DataType getOppositeSignednessDataType(); + public abstract AbstractIntegerDataType getOppositeSignednessDataType(); @Override public boolean isEquivalent(DataType dt) { @@ -452,21 +463,19 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt if (dataOrganization != null) { int index = dataOrganization.getLongLongSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = - (AbstractIntegerDataType) LongLongDataType.dataType.clone(dtm); + dataTypes[index] = LongLongDataType.dataType.clone(dtm); } index = dataOrganization.getLongSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = (AbstractIntegerDataType) LongDataType.dataType.clone(dtm); + dataTypes[index] = LongDataType.dataType.clone(dtm); } index = dataOrganization.getShortSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = (AbstractIntegerDataType) ShortDataType.dataType.clone(dtm); + dataTypes[index] = ShortDataType.dataType.clone(dtm); } index = dataOrganization.getIntegerSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = - (AbstractIntegerDataType) IntegerDataType.dataType.clone(dtm); + dataTypes[index] = IntegerDataType.dataType.clone(dtm); } } } @@ -523,26 +532,23 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt if (dataOrganization != null) { int index = dataOrganization.getLongLongSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = - (AbstractIntegerDataType) UnsignedLongLongDataType.dataType.clone(dtm); + dataTypes[index] = UnsignedLongLongDataType.dataType.clone(dtm); } index = dataOrganization.getLongSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = - (AbstractIntegerDataType) UnsignedLongDataType.dataType.clone(dtm); + dataTypes[index] = UnsignedLongDataType.dataType.clone(dtm); } index = dataOrganization.getShortSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = - (AbstractIntegerDataType) UnsignedShortDataType.dataType.clone(dtm); + dataTypes[index] = UnsignedShortDataType.dataType.clone(dtm); } index = dataOrganization.getIntegerSize() - 1; if (index >= 0 && index < 8) { - dataTypes[index] = - (AbstractIntegerDataType) UnsignedIntegerDataType.dataType.clone(dtm); + dataTypes[index] = UnsignedIntegerDataType.dataType.clone(dtm); } } } return dataTypes; } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java new file mode 100644 index 0000000000..fd1b776826 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java @@ -0,0 +1,170 @@ +/* ### + * 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.program.model.data; + +import java.util.ArrayList; +import java.util.List; + +import javax.help.UnsupportedOperationException; + +import ghidra.docking.settings.Settings; +import ghidra.util.exception.DuplicateNameException; + +public class AlignedStructureInspector extends AlignedStructurePacker { + + private AlignedStructureInspector(Structure structure) { + super(structure, getComponentWrappers(structure)); + } + + private static List getComponentWrappers(Structure structure) { + List list = new ArrayList<>(); + for (DataTypeComponent c : structure.getComponents()) { + list.add(new ReadOnlyComponentWrapper(c)); + } + return list; + } + + private static class ReadOnlyComponentWrapper implements InternalDataTypeComponent { + + private final DataTypeComponent component; + + private int ordinal; + private int offset; + private int length; + private DataType dataType; + + ReadOnlyComponentWrapper(DataTypeComponent component) { + this.component = component; + this.ordinal = component.getOrdinal(); + this.offset = component.getOffset(); + this.length = component.getLength(); + this.dataType = component.getDataType(); + } + + @Override + public void update(int ordinal, int offset, int length) { + this.ordinal = ordinal; + this.offset = offset; + this.length = length; + } + + @Override + public DataType getDataType() { + return dataType; + } + + @Override + public DataType getParent() { + return component.getParent(); + } + + @Override + public boolean isFlexibleArrayComponent() { + return false; + } + + @Override + public boolean isBitFieldComponent() { + return component.isBitFieldComponent(); + } + + @Override + public boolean isZeroBitFieldComponent() { + return component.isZeroBitFieldComponent(); + } + + @Override + public int getOrdinal() { + return ordinal; + } + + @Override + public int getOffset() { + return offset; + } + + @Override + public int getEndOffset() { + return offset + length - 1; + } + + @Override + public int getLength() { + return length; + } + + @Override + public String getComment() { + return component.getComment(); + } + + @Override + public Settings getDefaultSettings() { + return component.getDefaultSettings(); + } + + @Override + public void setDefaultSettings(Settings settings) { + throw new UnsupportedOperationException(); + } + + @Override + public void setComment(String comment) { + throw new UnsupportedOperationException(); + } + + @Override + public String getFieldName() { + return component.getFieldName(); + } + + @Override + public void setFieldName(String fieldName) throws DuplicateNameException { + throw new UnsupportedOperationException(); + } + + @Override + public String getDefaultFieldName() { + if (isZeroBitFieldComponent()) { + return ""; + } + return DEFAULT_FIELD_NAME_PREFIX + "_0x" + Integer.toHexString(getOffset()); + } + + @Override + public boolean isEquivalent(DataTypeComponent dtc) { + throw new UnsupportedOperationException(); + } + + @Override + public void setDataType(DataType dataType) { + this.dataType = dataType; + } + + } + + /** + * Perform structure component packing in a read-only fashion primarily + * for the purpose of computing external alignment for existing structures. + * @param structure + * @return aligned packing result + */ + public static StructurePackResult packComponents(Structure structure) { + AlignedStructureInspector packer = new AlignedStructureInspector(structure); + return packer.pack(); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java new file mode 100644 index 0000000000..c1c5afc850 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java @@ -0,0 +1,591 @@ +/* ### + * 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.program.model.data; + +import java.util.Iterator; +import java.util.List; + +import ghidra.util.exception.AssertException; + +/** + * AlignedStructurePacker provides support for performing aligned packing + * of Structure components. + *

+ * NOTE: We currently have no way of conveying or supporting explicit bitfield component pragmas + * supported by some compilers (e.g., bit_field_size, bit_field_align, bit_packing). + */ +public class AlignedStructurePacker { + + private final Structure structure; + private final List components; + + private DataOrganization dataOrganization; + private BitFieldPacking bitFieldPacking; + + /** + * Constructor. + * @param structure structure whose components need to be packed and updated + * during packing (ordinal, offset, length and bit-field datatypes may be modified) + * @param components list of mutable component + */ + protected AlignedStructurePacker(Structure structure, + List components) { + this.structure = structure; + this.components = components; + dataOrganization = structure.getDataOrganization(); + bitFieldPacking = dataOrganization.getBitFieldPacking(); + } + + /** + * StructurePackResult provides access to aligned + * packing results + */ + public static class StructurePackResult { + public final int numComponents; + public final int structureLength; + public final int alignment; + public final boolean componentsChanged; + + StructurePackResult(int numComponents, int structureLength, int alignment, + boolean componentsChanged) { + this.numComponents = numComponents; + this.structureLength = structureLength; + this.alignment = alignment; + this.componentsChanged = componentsChanged; + } + } + + private class AlignedGroupPacker { + + private int nextOrdinal; + + // We hang onto the imposed alignment and resulting offset for the last component + // since the update of a zero-length bitfield must be deferred since it can be influenced + // by the next component and we only want to update it once. + private int zeroAlignment; // imposed alignment on next component by zero bitfield + + private int lastAlignment; // identifies last component alignment + + // The groupOffset is used to identify start of zero-length bitfield as well + // as fixed-length groups when groupSameSizeOnly is true. Under other situations + // its value can not be relied upon. -1 value indicates no active group. + private int groupOffset = -1; + + private InternalDataTypeComponent lastComponent; + + private boolean componentsChanged; + + void addComponent(InternalDataTypeComponent dataTypeComponent, boolean isLastComponent) { + if (!packComponent(dataTypeComponent)) { + initGroup(dataTypeComponent, isLastComponent); + } + lastComponent = dataTypeComponent; + ++nextOrdinal; + } + + int getLength() { + if (lastComponent == null) { + return 0; + } + int offset = 0; + if (groupOffset >= 0 && lastComponent.isBitFieldComponent() && + bitFieldPacking.useMSConvention()) { + // skip beyond unused bits based upon allocation size + BitFieldDataType lastBitFieldDt = (BitFieldDataType) lastComponent.getDataType(); + offset = groupOffset + lastBitFieldDt.getBaseTypeSize(); + } + else { + offset = lastComponent.getOffset() + lastComponent.getLength(); + if (!bitFieldPacking.useMSConvention() && lastComponent.isZeroBitFieldComponent()) { + // factor in trailing zero-length bitfield + BitFieldDataType bitfieldDt = (BitFieldDataType) lastComponent.getDataType(); + int sizeAlignment = CompositeAlignmentHelper.getPackedAlignment( + dataOrganization, Composite.NOT_PACKING, bitfieldDt.getBaseDataType(), + bitfieldDt.getBaseTypeSize()); + getBitFieldAlignment((BitFieldDataType) lastComponent.getDataType()); + offset += DataOrganizationImpl.getPaddingSize(sizeAlignment, offset); + } + } + return offset; + } + + private int getBitFieldTypeSize(InternalDataTypeComponent dataTypeComponent) { + DataType componentDt = dataTypeComponent.getDataType(); + if (componentDt instanceof BitFieldDataType) { + return ((BitFieldDataType) componentDt).getBaseTypeSize(); + } + throw new AssertException("expected bitfield component only"); + } + + private int getBitFieldAlignment(BitFieldDataType bitfieldDt) { + + int packValue = structure.getPackingValue(); + + if (!bitFieldPacking.useMSConvention() && packValue != Composite.NOT_PACKING) { + // GCC always uses 1 when packing regardless of pack value + return 1; + } + + return CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packValue, + bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize()); + } + + private boolean isIgnoredZeroBitField(BitFieldDataType zeroBitFieldDt) { + if (!zeroBitFieldDt.isZeroLengthField()) { + return false; + } + if (bitFieldPacking.useMSConvention()) { + // TODO: verify when :0 is first component + return lastComponent == null || !lastComponent.isBitFieldComponent(); + } + return false; + } + + private int getZeroBitFieldAlignment(BitFieldDataType zeroBitFieldDt, + boolean isLastComponent) { + + if (isIgnoredZeroBitField(zeroBitFieldDt)) { + return -1; + } + + if (!bitFieldPacking.isTypeAlignmentEnabled()) { + int zeroLengthBitFieldBoundary = bitFieldPacking.getZeroLengthBoundary(); + if (zeroLengthBitFieldBoundary > 0) { + return zeroLengthBitFieldBoundary; + } + return 1; + } + + int packValue = structure.getPackingValue(); + + if (!bitFieldPacking.useMSConvention() && !isLastComponent) { + // GCC ignores pack value for :0 bitfield alignment but considers it when + // passing alignment along to structure + packValue = Composite.NOT_PACKING; + } + + return CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packValue, + zeroBitFieldDt.getBaseDataType(), zeroBitFieldDt.getBaseTypeSize()); + } + + private void initGroup(InternalDataTypeComponent dataTypeComponent, + boolean isLastComponent) { + + groupOffset = getLength(); + lastAlignment = 1; + + if (dataTypeComponent.isBitFieldComponent()) { + + BitFieldDataType zeroBitFieldDt = + (BitFieldDataType) dataTypeComponent.getDataType(); + + if (dataTypeComponent.isZeroBitFieldComponent()) { + + // An alignment of -1 indicates field is ignored + int alignment = getZeroBitFieldAlignment(zeroBitFieldDt, isLastComponent); + + int zeroBitOffset = dataOrganization.isBigEndian() ? 7 : 0; + if (zeroBitFieldDt.getBitOffset() != zeroBitOffset || + zeroBitFieldDt.getStorageSize() != 1) { + try { + BitFieldDataType packedBitFieldDt = new BitFieldDataType( + zeroBitFieldDt.getBaseDataType(), 0, zeroBitOffset, 1); + dataTypeComponent.setDataType(packedBitFieldDt); + } + catch (InvalidDataTypeException e) { + throw new AssertException("unexpected", e); + } + componentsChanged = true; + } + + if (isLastComponent) { + // special handling of zero-length bitfield when it is last component, + // place on last byte within structure. + int offset = groupOffset; + int length = 1; + if (lastComponent != null) { + offset = groupOffset - 1; // lastComponent.getEndOffset(); + } + updateComponent(dataTypeComponent, nextOrdinal, offset, length, + alignment > 0 ? alignment : 1); + groupOffset = -1; + } + else { + // Avoid conveying zero alignment onto structure + // Next component can influence alignment + zeroAlignment = alignment; + + // NOTE: MSVC always conveys zero-length alignment + if (bitFieldPacking.useMSConvention()) { + lastAlignment = alignment; + } + + // defer update of zero-length component and final determination of alignment. + } + } + else { + lastComponent = null; // first in allocation group + alignAndPackBitField(dataTypeComponent); // relies on groupOffset when lastComponent==null + } + } + else { + // pack non-bitfield + lastComponent = null; // first in allocation group + alignAndPackNonBitfieldComponent(dataTypeComponent, groupOffset); + } + } + + /** + * Adjust lastComponent which must be a zero-length bitfield and its associated + * groupOffset based upon the adjusted alignment. + * @param ordinal component ordinal assignment + * @param minimumAlignment minimum alignment of component immediately after + * the zero-length component + */ + private void adjustZeroLengthBitField(int ordinal, int minimumAlignment) { + + int minOffset = DataOrganizationImpl.getOffset(minimumAlignment, groupOffset); + int zeroAlignmentOffset = DataOrganizationImpl.getOffset(zeroAlignment, groupOffset); + + // Determine component offset of zero-length bitfield and the component + // which immediately follows it. + + if (minOffset >= zeroAlignmentOffset) { + // natural offset satisfies :0 alignment + groupOffset = minOffset; + } + else { + groupOffset = zeroAlignmentOffset; + } + + updateComponent(lastComponent, ordinal, groupOffset, 1, minimumAlignment); + } + + private boolean canPack(DataType dataType) { + if (dataType instanceof TypeDef) { + dataType = ((TypeDef) dataType).getBaseDataType(); + } + if (dataType instanceof BitFieldDataType) { + return true; + } + if (dataType instanceof AbstractIntegerDataType) { + return true; + } + if (dataType instanceof Enum) { + return true; + } + return false; + } + + private boolean packComponent(InternalDataTypeComponent dataTypeComponent) { + + if (lastComponent == null || dataTypeComponent.isZeroBitFieldComponent() || + !canPack(lastComponent.getDataType()) || + !canPack(dataTypeComponent.getDataType())) { + return false; // can't pack incompatible types - start new group + } + + if (dataTypeComponent.isBitFieldComponent()) { + + if (!lastComponent.isZeroBitFieldComponent() && bitFieldPacking.useMSConvention()) { + if (!lastComponent.isBitFieldComponent()) { + return false; // can't pack bitfield with non-bitfield - start new group + } + if (getBitFieldTypeSize(dataTypeComponent) != getBitFieldTypeSize( + lastComponent)) { + return false; // bitfield base types differ in size - start new group + } + } + + alignAndPackBitField(dataTypeComponent); // relies on lastComponent + + return true; + } + + if (!lastComponent.isZeroBitFieldComponent() && bitFieldPacking.useMSConvention()) { + return false; // start new group for non-bitfield + } + + int offset; + if (lastComponent.isZeroBitFieldComponent()) { + offset = groupOffset; + } + else { + offset = lastComponent.getOffset() + lastComponent.getLength(); + } + + alignAndPackNonBitfieldComponent(dataTypeComponent, offset); + + return true; + } + + private void alignAndPackNonBitfieldComponent(InternalDataTypeComponent dataTypeComponent, + int minOffset) { + DataType componentDt = dataTypeComponent.getDataType(); + int dtSize = componentDt.getLength(); + if (dtSize <= 0) { + dtSize = dataTypeComponent.getLength(); + } + + int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, + structure.getPackingValue(), componentDt, dtSize); + + int offset; + if (lastComponent != null && lastComponent.isZeroBitFieldComponent()) { + // adjust group alignment and offset of zero-length component (properly aligns groupOffset) + adjustZeroLengthBitField(nextOrdinal - 1, alignment); + offset = groupOffset; + } + else { + offset = DataOrganizationImpl.getOffset(alignment, minOffset); + if (lastComponent == null) { + groupOffset = offset; // establish corrected group offset after alignment + } + } + + updateComponent(dataTypeComponent, nextOrdinal, offset, dtSize, alignment); + } + + private void alignAndPackBitField(InternalDataTypeComponent dataTypeComponent) { + + BitFieldDataType bitfieldDt = (BitFieldDataType) dataTypeComponent.getDataType(); + + if (lastComponent != null && lastComponent.isZeroBitFieldComponent()) { + int alignment = bitFieldPacking.useMSConvention() ? getBitFieldAlignment(bitfieldDt) + : zeroAlignment; + adjustZeroLengthBitField(nextOrdinal - 1, alignment); + } + + int offset; + int bitsConsumed; + + // update lastAlignment to be conveyed onto structure alignment + int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, + structure.getPackingValue(), bitfieldDt.getPrimitiveBaseDataType(), + bitfieldDt.getBaseTypeSize()); + + // Set conveyed alignment early since bitfield alignment may be reduced below + lastAlignment = Math.max(alignment, lastAlignment); + + if (lastComponent == null) { + offset = DataOrganizationImpl.getOffset(alignment, groupOffset); + bitsConsumed = 0; + groupOffset = offset; // establish corrected group offset after alignment + } + else if (lastComponent.isZeroBitFieldComponent()) { + // - assume lastComponent (zero-length bitfield) has already been adjusted and updated + // - first bitfield following a :0 bitfield which has already been adjusted by packComponent + // - groupOffset contains aligned offset to be used + offset = groupOffset; + bitsConsumed = 0; + } + else { + + // follow normal rule for aligning bitfield which may differ from the alignment + // imparted onto structure via lastAlignment + alignment = getBitFieldAlignment(bitfieldDt); + + BitFieldDataType lastBitfieldDt = null; + if (lastComponent.isBitFieldComponent()) { + + // assume lastComponent bit-field has already been packed and has correct bit-offset + + lastBitfieldDt = (BitFieldDataType) lastComponent.getDataType(); + offset = lastComponent.getEndOffset(); + if (dataOrganization.isBigEndian()) { + // filled left-to-right + bitsConsumed = 8 - lastBitfieldDt.getBitOffset(); + // bitsConsumed range: 1 to 8, where 8 indicates last byte fully consumed + } + else { // little-endian + // filled right-to-left (viewed from normalized form after byte-swap) + bitsConsumed = + (lastBitfieldDt.getBitSize() + lastBitfieldDt.getBitOffset()) % 8; + // bitsConsumed range: 0 to 7, where 0 indicates last byte fully consumed + } + if (bitsConsumed == 8 || bitsConsumed == 0) { + // last byte is fully consumed + bitsConsumed = 0; + ++offset; + } + + } + else { + // previous field is non-bitfield + offset = lastComponent.getOffset() + lastComponent.getLength(); + bitsConsumed = 0; + } + + int byteSize = (bitfieldDt.getBitSize() + bitsConsumed + 7) / 8; + int endOffset = offset + byteSize - 1; + + if (offset % alignment != 0 || byteSize > bitfieldDt.getBaseTypeSize()) { + // offset is not an aligned offset (which may be OK when packing with lastComponent) + int alignedBaseOffset = + DataOrganizationImpl.getOffset(alignment, offset) - alignment; + if (endOffset >= alignedBaseOffset + bitfieldDt.getBaseTypeSize()) { + // skip ahead to next aligned offset + offset = DataOrganizationImpl.getOffset(alignment, offset + 1); + endOffset = offset + byteSize - 1; + bitsConsumed = 0; + } + } + + // establish new groupOffset if necessary + if (groupOffset >= 0 && lastBitfieldDt != null && + endOffset >= (groupOffset + lastBitfieldDt.getBaseTypeSize())) { + groupOffset = bitFieldPacking.useMSConvention() ? offset : -1; + } + } + + int byteSize = setBitFieldDataType(dataTypeComponent, bitfieldDt, bitsConsumed); + + updateComponent(dataTypeComponent, nextOrdinal, offset, byteSize, alignment); + } + + private int setBitFieldDataType(InternalDataTypeComponent dataTypeComponent, + BitFieldDataType currentBitFieldDt, int bitsConsumed) { + int byteSize = (currentBitFieldDt.getBitSize() + bitsConsumed + 7) / 8; + int bitOffset; + if (dataOrganization.isBigEndian()) { + // filled left-to-right + bitOffset = (byteSize * 8) - currentBitFieldDt.getBitSize() - bitsConsumed; + } + else { // little-endian + // filled right-to-left (viewed from normalized form after byte-swap) + bitOffset = bitsConsumed; + } + + if (bitOffset != currentBitFieldDt.getBitOffset() || + byteSize != currentBitFieldDt.getStorageSize()) { + try { + BitFieldDataType packedBitFieldDt = + new BitFieldDataType(currentBitFieldDt.getBaseDataType(), + currentBitFieldDt.getDeclaredBitSize(), bitOffset, byteSize); + dataTypeComponent.setDataType(packedBitFieldDt); + } + catch (InvalidDataTypeException e) { + throw new AssertException("unexpected", e); + } + componentsChanged = true; + } + return byteSize; + } + + private void updateComponent(InternalDataTypeComponent dataTypeComponent, int ordinal, + int offset, int length, int alignment) { + + if (ordinal != dataTypeComponent.getOrdinal() || + offset != dataTypeComponent.getOffset() || + length != dataTypeComponent.getLength()) { + + dataTypeComponent.update(ordinal, offset, length); + componentsChanged = true; + } + + lastAlignment = Math.max(lastAlignment, alignment); + } + + int getComponentAlignmentLCM(int allComponentsLCM) { + + if (lastAlignment == 0) { + return lastAlignment; + } + + // factor in pack value, which may have been ignored when aligning component + int alignment = lastAlignment; + int packValue = structure.getPackingValue(); + if (packValue > 0 && alignment > packValue) { + alignment = packValue; + } + return DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, alignment); + } + + } + + protected StructurePackResult pack() { + + boolean componentsChanged = false; + + int componentCount = 0; + int allComponentsLCM = 1; + + AlignedGroupPacker packer = new AlignedGroupPacker(); + + // Remove any default components from list + Iterator componentIterator = components.iterator(); + while (componentIterator.hasNext()) { + InternalDataTypeComponent dataTypeComponent = componentIterator.next(); + DataType componentDt = dataTypeComponent.getDataType(); + if (DataType.DEFAULT == componentDt) { + componentIterator.remove(); // remove default components. + componentsChanged = true; + } + ++componentCount; + } + + int index = 0; + for (InternalDataTypeComponent dataTypeComponent : components) { + boolean isLastComponent = (++index == componentCount); + packer.addComponent(dataTypeComponent, isLastComponent); + allComponentsLCM = packer.getComponentAlignmentLCM(allComponentsLCM); + } + + int length = packer.getLength(); + componentsChanged |= packer.componentsChanged; + + DataTypeComponent flexibleArrayComponent = structure.getFlexibleArrayComponent(); + if (flexibleArrayComponent != null) { + // account for flexible array type and any end of structure padding required + int componentAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, + structure.getPackingValue(), flexibleArrayComponent); + length = DataOrganizationImpl.getOffset(componentAlignment, length); + allComponentsLCM = + DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, componentAlignment); + } + + int alignment = structure.getMinimumAlignment(); + if (alignment < allComponentsLCM) { + alignment = allComponentsLCM; + } + + if (length != 0) { + int padSize = DataOrganizationImpl.getPaddingSize(alignment, length); + if (padSize > 0) { + length += padSize; + } + } + + return new StructurePackResult(componentCount, length, alignment, componentsChanged); + } + + /** + * Perform structure component packing. Specified components may be updated to reflect + * packing (ordinal, offset, length and bit-field datatypes may be modified). The caller + * is responsible for updating structure length and component count based upon + * returned result. Component count is should only change if component + * list includes DEFAULT members which will be ignored. + * @param structure structure whose members are to be aligned/packed. + * @param components structure components (excludes any trailing flexible array). + * @return aligned packing result + */ + public static StructurePackResult packComponents(Structure structure, + List components) { + AlignedStructurePacker packer = new AlignedStructurePacker(structure, components); + return packer.pack(); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java index ce81f3927b..b0fddaf6f0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java @@ -169,7 +169,7 @@ public class ArrayDataType extends DataTypeImpl implements Array { } @Override - public void setName(String name) throws InvalidNameException, DuplicateNameException { + public void setName(String name) throws InvalidNameException { // unsupported - ignore } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java new file mode 100644 index 0000000000..61dda1d869 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java @@ -0,0 +1,417 @@ +/* ### + * 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.program.model.data; + +import java.math.BigInteger; + +import ghidra.docking.settings.Settings; +import ghidra.docking.settings.SettingsDefinition; +import ghidra.program.model.mem.MemBuffer; +import ghidra.program.model.scalar.Scalar; +import ghidra.util.exception.AssertException; + +/** + * BitFieldDataType provides a means of defining a minimally sized bit-field + * for use within data structures. The length (i.e., storage size) of this datatype + * less than or equal to the base datatype size and will always be the smallest possible size + * to contain the bitfield and offset within the least significant byte containing the + * lsb of the bitfield. + */ +public class BitFieldDataType extends AbstractDataType { + + private static final int MAX_BIT_LENGTH = 255; + + private final DataType baseDataType; // bit-field definition data type which corresponds to baseType + private final int bitSize; // number of bits, reflects declaration which may exceed base type size + private final int effectiveBitSize; // number of bits constrained by size of base type + + // The bitOffset is established during packing and reflects the right-shift amount within the + // normalized big-endian view of the allocated byte storage as defined by the corresponding + // composite DataTypeComponent. + private final int bitOffset; // indicates right-shift within big-endian view of component storage + private final int storageSize; // component storage size to which bitOffset applies + + protected Settings defaultSettings; + + /** + * Construct a bit-field type based upon a specified base type. The baseDataType will + * take precedence if specified. Either baseType or baseDatatype must be specified. + * @param baseDataType base data type (integer/enum type or typedef to same) + * @param bitSize size of bit-field expressed as number of bits + * @param bitOffset right shift factor within storage unit when viewed as a big-endian dd + * scalar value. Based upon minimal storage bitOffset should be in the range 0 to 7. + * @param storageSize minimal storage allocation to which bitOffset is applied or 0 to use + * minimum storage size. + * @throws InvalidDataTypeException + */ +// FIXME: Remove storage parameter (compute based upon bitSize and bitOffset) + protected BitFieldDataType(DataType baseDataType, int bitSize, int bitOffset, int storageSize) + throws InvalidDataTypeException { + super(CategoryPath.ROOT, baseDataType.getName() + ":" + bitSize, + baseDataType.getDataTypeManager()); + checkBaseDataType(baseDataType); + if (bitSize < 0 || bitSize > MAX_BIT_LENGTH) { + throw new InvalidDataTypeException("unsupported bit size: " + bitSize); + } + if (bitOffset < 0 || bitOffset > 7) { + throw new InvalidDataTypeException("unsupported minimal bit offset: " + bitOffset); + } + this.baseDataType = baseDataType; + this.bitSize = bitSize; + this.bitOffset = bitOffset; + effectiveBitSize = getEffectiveBitSize(bitSize, this.baseDataType.getLength()); + if (storageSize == 0) { + storageSize = getMinimumStorageSize(effectiveBitSize, bitOffset); + } + this.storageSize = storageSize; + checkStorage(); + if (bitOffset < 0 || bitOffset > ((8 * storageSize) - effectiveBitSize)) { + throw new InvalidDataTypeException("invalid bit offset: " + bitOffset); + } + this.defaultSettings = this.baseDataType.getDefaultSettings(); + } + + /** + * 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. + * @param bitSize size of bit-field expressed as number of bits + * @throws InvalidDataTypeException if specified baseDataType is not permitted + */ + protected BitFieldDataType(DataType baseDataType, int bitSize) throws InvalidDataTypeException { + this(baseDataType, bitSize, 0, 0); + } + + private void checkStorage() throws IllegalArgumentException { + int minimumStorageSize = getMinimumStorageSize(effectiveBitSize); + if (storageSize != minimumStorageSize && storageSize != ++minimumStorageSize) { + throw new IllegalArgumentException("minimal storage size required"); + } + } + + /** + * Determine if this bit-field has a zero length (i.e., alignment field) + * @return true if this bit-field has a zero length + */ + public boolean isZeroLengthField() { + return bitSize == 0; + } + + /** + * Get the effective bit-size based upon the specified base type size. A bit size + * larger than the base type size will truncated to the base type size. + * @param declaredBitSize + * @param baseTypeByteSize + * @return effective bit-size + */ + public static int getEffectiveBitSize(int declaredBitSize, int baseTypeByteSize) { + return Math.min(8 * baseTypeByteSize, declaredBitSize); + } + + /** + * Get the minimum storage size in bytes for a given size in bits. + * This does not consider the bit offset which may increase the required + * storage. + * @param bitSize number of bits within bitfield + * @return minimum storage size in bytes + */ + public static int getMinimumStorageSize(int bitSize) { + return getMinimumStorageSize(bitSize, 0); + } + + /** + * Get the minimum storage size in bytes for a given size in bits with + * the specified bitOffset (lsb position within big endian storage) + * @param bitSize number of bits within bitfield + * @param bitOffset normalized bitfield offset within storage (lsb) + * @return minimum storage size in bytes + */ + public static int getMinimumStorageSize(int bitSize, int bitOffset) { + if (bitSize == 0) { + return 1; + } + return (bitSize + (bitOffset % 8) + 7) / 8; + } + + /** + * Check a bitfield base datatype + * @param baseDataType bitfield base data type (Enum, AbstractIntegerDataType and derived TypeDefs permitted) + * @throws InvalidDataTypeException if baseDataType is invalid as a bitfield base type. + */ + public static void checkBaseDataType(DataType baseDataType) throws InvalidDataTypeException { + if (!isValidBaseDataType(baseDataType)) { + throw new InvalidDataTypeException( + "Unsupported base data type for bitfield: " + baseDataType.getName()); + } + } + + /** + * Check if a specified baseDataType is valid for use with a bitfield + * @param baseDataType bitfield base data type (Enum, AbstractIntegerDataType and derived TypeDefs permitted) + * @returns true if baseDataType is valid else false + */ + public static boolean isValidBaseDataType(DataType baseDataType) { + if (baseDataType instanceof TypeDef) { + baseDataType = ((TypeDef) baseDataType).getBaseDataType(); + } + if (baseDataType instanceof Enum) { + return true; + } + if (baseDataType instanceof AbstractIntegerDataType) { + return true; + } + return false; + } + + @Override + public void addParent(DataType dt) { + if ((baseDataType instanceof TypeDef) || (baseDataType instanceof Enum)) { + baseDataType.addParent(dt); // add composite as parent of baseDataType + } + } + + /** + * Get the size of the base data type based upon the associated data organization. + * @return base type size + */ + public int getBaseTypeSize() { + return baseDataType.getLength(); + } + + /** + * Get the packing storage size in bytes associated with this bit-field which may be + * larger than the base type associated with the fields original definition. + * @return packing storage size in bytes + */ + public int getStorageSize() { + return storageSize; + } + + /** + * Get the effective bit size of this bit-field which may not exceed the size of the + * base datatype. + * @return bit size + */ + public int getBitSize() { + return effectiveBitSize; + } + + /** + * Get the declared bit size of this bit-field which may be larger than the effective + * size which could be truncated. + * @return bit size as defined by the field construction/declaration. + */ + public int getDeclaredBitSize() { + return bitSize; + } + + /** + * Get the bit offset of the least-significant bit relative to bit-0 of the + * base datatype (i.e., least significant bit). This corresponds to the + * right-shift amount within the base data type when viewed as a big-endian value. + * @return bit offset + */ + public int getBitOffset() { + return bitOffset; + } + + /** + * Get the base datatype associated with this bit-field + * (e.g., int, long, etc., or TypeDef to supported base type) + * @return base data type + */ + public DataType getBaseDataType() { + return baseDataType; + } + + /** + * Get the base datatype associated with this bit-field + * (e.g., int, long, etc., or TypeDef to supported base type) + * @return base data type + */ + public AbstractIntegerDataType getPrimitiveBaseDataType() { + // assumes proper enforcement during construction + DataType dt = baseDataType; + if (baseDataType instanceof TypeDef) { + dt = ((TypeDef) baseDataType).getBaseDataType(); + } + if (dt instanceof Enum) { + // TODO: uncertain if we should use signed or unsigned, although size + // is most important + dt = AbstractIntegerDataType.getUnsignedDataType(((Enum) dt).getLength(), dataMgr); + } + return (AbstractIntegerDataType) dt; + } + + /** + * Gets a list of all the settingsDefinitions used by this datatype. + * @return a list of the settingsDefinitions used by this datatype. + */ + @Override + public final SettingsDefinition[] getSettingsDefinitions() { + return baseDataType.getSettingsDefinitions(); + } + + @Override + public final boolean isEquivalent(DataType dt) { + if (dt == this) { + return true; + } + if (dt == null) { + return false; + } + if (!(dt instanceof BitFieldDataType)) { + return false; + } + BitFieldDataType otherBitField = (BitFieldDataType) dt; + // Specific packing and use of typedef ignored for equivalence check + return otherBitField.bitSize == bitSize && + baseDataType.isEquivalent(otherBitField.baseDataType); + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + baseDataType.hashCode(); + result = prime * result + bitOffset; + result = prime * result + bitSize; + return result; + } + + @Override + public final boolean equals(Object obj) { + if (!(obj instanceof BitFieldDataType)) { + return false; + } + BitFieldDataType otherDt = (BitFieldDataType) obj; + return (otherDt.getDataTypeManager() == getDataTypeManager()) && isEquivalent(otherDt) && + (bitOffset == otherDt.bitOffset) && (storageSize == otherDt.storageSize) && + baseDataType.equals(otherDt.baseDataType); + } + + @Override + public Settings getDefaultSettings() { + return defaultSettings; + } + + /** + * Returns a clone of this built-in DataType + * @see ghidra.program.model.data.DataType#copy(ghidra.program.model.data.DataTypeManager) + */ + @Override + public final DataType copy(DataTypeManager dtm) { + return clone(dtm); + } + + @Override + public BitFieldDataType clone(DataTypeManager dtm) { + if (dtm == dataMgr) { + return this; + } + try { + return new BitFieldDataType(baseDataType.clone(dtm), bitSize, bitOffset, storageSize); + } + catch (InvalidDataTypeException e) { + throw new AssertException("unexpected", e); + } + } + + @Override + public int getLength() { + return storageSize; + } + + @Override + public String getDescription() { + return getName() + " BitField"; + } + + @Override + public Object getValue(MemBuffer buf, Settings settings, int length) { + if (effectiveBitSize == 0) { + return new Scalar(0, 0); + } + BigInteger big = getBigIntegerValue(buf, settings); + if (big == null) { + return null; + } + if (effectiveBitSize <= 64) { + return new Scalar(effectiveBitSize, big.longValue(), + getPrimitiveBaseDataType().isSigned()); + } + return big; + } + + public BigInteger getBigIntegerValue(MemBuffer buf, Settings settings) { + if (effectiveBitSize == 0) { + return BigInteger.ZERO; + } + try { + BigInteger big = buf.getBigInteger(0, storageSize, false); + BigInteger pow = BigInteger.valueOf(2).pow(effectiveBitSize); + BigInteger mask = pow.subtract(BigInteger.ONE); + big = big.shiftRight(bitOffset).and(mask); + if (big.testBit(effectiveBitSize - 1)) { + big = big.subtract(pow); + } + return big; + } + catch (Exception e) { + // ignore + } + return null; + } + + @Override + public Class getValueClass(Settings settings) { + return baseDataType.getValueClass(settings); + } + + @Override + public String getRepresentation(MemBuffer buf, Settings settings, int length) { + if (bitSize == 0) { + return ""; + } + BigInteger big = getBigIntegerValue(buf, settings); + if (big == null) { + return "??"; + } + DataType dt = baseDataType; + if (dt instanceof TypeDef) { + dt = ((TypeDef) dt).getBaseDataType(); + } + if (dt instanceof Enum) { + return ((Enum) dt).getRepresentation(big, settings, effectiveBitSize); + } + return ((AbstractIntegerDataType) dt).getRepresentation(big, settings, effectiveBitSize); + } + + @Override + public void setDefaultSettings(Settings settings) { + this.defaultSettings = settings; + } + + @Override + public int getAlignment() { + return baseDataType.getAlignment(); + } + + @Override + public String toString() { + return getDisplayName() + "(storage:" + storageSize + ",bitOffset:" + bitOffset + ")"; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPacking.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPacking.java new file mode 100644 index 0000000000..5d5a94bf34 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPacking.java @@ -0,0 +1,47 @@ +/* ### + * 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.program.model.data; + +public interface BitFieldPacking { + + /** + * Control if the alignment and packing of bit-fields follows MSVC conventions. + * When this is enabled it takes precedence over all other bitfield packing controls. + * @return true if MSVC packing conventions are used, else false (e.g., GNU conventions apply). + */ + boolean useMSConvention(); + + /** + * Control whether the alignment of bit-field types is respected when laying out structures. + * Corresponds to PCC_BITFIELD_TYPE_MATTERS in GCC. + * @return true when the alignment of the bit-field type should be used to impact the + * alignment of the containing structure, and ensure that individual bit-fields will not + * straddle an alignment boundary. + */ + boolean isTypeAlignmentEnabled(); + + /** + * A non-zero value indicates the fixed alignment size for bit-fields which follow + * a zero-length bitfield if greater than a bitfields base type normal alignment. + * Corresponds to EMPTY_FIELD_BOUNDARY in GCC. + * This value is only used when {@link #isTypeAlignmentEnabled()} returns false + * and {@link #isZeroLengthAlignmentEnabled()} returns true. + * @return fixed alignment size as number of bytes for a bit-field which follows + * a zero-length bit-field + */ + int getZeroLengthBoundary(); + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java new file mode 100644 index 0000000000..cc5cab0935 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java @@ -0,0 +1,68 @@ +/* ### + * 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.program.model.data; + +public class BitFieldPackingImpl implements BitFieldPacking { + + private boolean useMSConvention = false; + private boolean typeAlignmentEnabled = true; + private int zeroLengthBoundary = 0; + + @Override + public boolean useMSConvention() { + return useMSConvention; + } + + @Override + public boolean isTypeAlignmentEnabled() { + return useMSConvention || typeAlignmentEnabled; // same as PCC_BITFIELD_TYPE_MATTERS + } + + @Override + public int getZeroLengthBoundary() { + return useMSConvention ? 0 : zeroLengthBoundary; + } + + /** + * Control if the alignment and packing of bit-fields follows MSVC conventions. + * When this is enabled it takes precedence over all other bitfield packing controls. + * @param useMSConvention true if MSVC packing conventions are used, else false (e.g., GNU conventions apply). + */ + public void setUseMSConvention(boolean useMSConvention) { + this.useMSConvention = useMSConvention; + } + + /** + * Control whether the alignment of bit-field types is respected when laying out structures. + * Corresponds to PCC_BITFIELD_TYPE_MATTERS in gcc. + * @param typeAlignmentEnabled true if the alignment of the bit-field type should be used + * to impact the alignment of the containing structure, and ensure that individual bit-fields + * will not straddle an alignment boundary. + */ + public void setTypeAlignmentEnabled(boolean typeAlignmentEnabled) { + this.typeAlignmentEnabled = typeAlignmentEnabled; + } + + /** + * Indicate a fixed alignment size in bytes which should be used for zero-length bit-fields. + * @param zeroLengthBoundary fixed alignment size as number of bytes for a bit-field + * which follows a zero-length bit-field. A value of 0 causes zero-length type size to be used. + */ + public void setZeroLengthBoundary(int zeroLengthBoundary) { + this.zeroLengthBoundary = zeroLengthBoundary; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltIn.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltIn.java index 4cc92a18fc..5ebe7b7245 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltIn.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltIn.java @@ -30,27 +30,28 @@ import ghidra.util.exception.DuplicateNameException; * searched for in the classpath and added automatically to the available * data types in the data type manager. */ -public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { - - private static SettingsDefinition[] STANDARD_SETTINGS_DEFINITIONS = new SettingsDefinition[] { - MutabilitySettingsDefinition.DEF - }; - +public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { + + private static SettingsDefinition[] STANDARD_SETTINGS_DEFINITIONS = + new SettingsDefinition[] { MutabilitySettingsDefinition.DEF }; + private SettingsDefinition[] settingDefs; public BuiltIn(CategoryPath path, String name, DataTypeManager dataMgr) { // Change any category path so that it is under the built in category. - super(path==null ? CategoryPath.ROOT : path, name, null, BuiltInSourceArchive.INSTANCE, NO_SOURCE_SYNC_TIME, NO_LAST_CHANGE_TIME, dataMgr); + super(path == null ? CategoryPath.ROOT : path, name, null, BuiltInSourceArchive.INSTANCE, + NO_SOURCE_SYNC_TIME, NO_LAST_CHANGE_TIME, dataMgr); } - + /** * Returns a clone of this built-in DataType * @see ghidra.program.model.data.DataType#copy(ghidra.program.model.data.DataTypeManager) */ + @Override public final DataType copy(DataTypeManager dtm) { return clone(dtm); } - + /** * Gets a list of all the settingsDefinitions used by this datatype. * @return a list of the settingsDefinitions used by this datatype. @@ -82,19 +83,19 @@ public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { } return getClass() == dt.getClass(); } - + @Override public void dataTypeSizeChanged(DataType dt) { // Default implementation does nothing. } - + @Override public final void setCategoryPath(CategoryPath path) throws DuplicateNameException { // Default implementation does nothing. } @Override - public final void setName(String name) throws InvalidNameException, DuplicateNameException { + public final void setName(String name) throws InvalidNameException { // Default implementation does nothing. } @@ -105,12 +106,12 @@ public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { } @Override - public final void addParent(DataType dt) { + public final void addParent(DataType dt) { // Default implementation does nothing. } @Override - public final void removeParent(DataType dt) { + public final void removeParent(DataType dt) { // Default implementation does nothing. } @@ -118,7 +119,7 @@ public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { public void dataTypeNameChanged(DataType dt, String oldName) { // Default implementation does nothing. } - + @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { // Default implementation does nothing. @@ -137,12 +138,12 @@ public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { // dt.dataMgr = dataManager; // return dt; // } - + @Override public boolean dependsOn(DataType dt) { return false; } - + @Override public UniversalID getUniversalID() { return null; @@ -163,25 +164,29 @@ public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { } protected String getCTypeDeclaration(String typeName, String ctypeName, boolean useDefine) { - return useDefine ? - "#define " + typeName + " " + ctypeName : - "typedef " + ctypeName + " " + typeName + ";"; + return useDefine ? "#define " + typeName + " " + ctypeName + : "typedef " + ctypeName + " " + typeName + ";"; } - - protected String getCTypeDeclaration(String typeName, int typeLen, boolean signed, DataOrganization dataOrganization, boolean useDefine) { - return getCTypeDeclaration(typeName, dataOrganization.getIntegerCTypeApproximation(typeLen, signed), useDefine); + + protected String getCTypeDeclaration(String typeName, int typeLen, boolean signed, + DataOrganization dataOrganization, boolean useDefine) { + return getCTypeDeclaration(typeName, + dataOrganization.getIntegerCTypeApproximation(typeLen, signed), useDefine); } - - protected String getCTypeDeclaration(BuiltIn dt, boolean signed, DataOrganization dataOrganization, boolean useDefine) { - return getCTypeDeclaration(dt.getDecompilerDisplayName(DecompilerLanguage.C_LANGUAGE), dataOrganization.getIntegerCTypeApproximation(dt.getLength(), signed), useDefine); + + protected String getCTypeDeclaration(BuiltIn dt, boolean signed, + DataOrganization dataOrganization, boolean useDefine) { + return getCTypeDeclaration(dt.getDecompilerDisplayName(DecompilerLanguage.C_LANGUAGE), + dataOrganization.getIntegerCTypeApproximation(dt.getLength(), signed), useDefine); } - + /** * Returns null for FactoryDataType (which should never be used) and Dynamic types which should * generally be replaced by a primitive array (e.g., char[5]) or, a primitive pointer (e.g., char *). * For other types an appropriately sized unsigned integer typedef is returned. * @see ghidra.program.model.data.BuiltInDataType#getCTypeDeclaration(ghidra.program.model.data.DataOrganization) */ + @Override public String getCTypeDeclaration(DataOrganization dataOrganization) { if ((this instanceof Dynamic) || (this instanceof FactoryDataType)) { return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java index 8a32888a67..de99b576dc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java @@ -35,10 +35,12 @@ public class ByteDataType extends AbstractIntegerDataType { super("byte", false, dtm); } + @Override public String getDescription() { return "Unsigned Byte (db)"; } + @Override public int getLength() { return 1; } @@ -56,11 +58,12 @@ public class ByteDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return SignedByteDataType.dataType; + public SignedByteDataType getOppositeSignednessDataType() { + return SignedByteDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public ByteDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java index fc204785ce..5d63a7a8e9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java @@ -138,7 +138,10 @@ public class CharDataType extends AbstractIntegerDataType implements DataTypeWit } @Override - public DataType clone(DataTypeManager dtm) { + public CharDataType clone(DataTypeManager dtm) { + if (dtm == getDataTypeManager()) { + return this; + } return new CharDataType(dtm); } @@ -176,8 +179,9 @@ public class CharDataType extends AbstractIntegerDataType implements DataTypeWit } @Override - public DataType getOppositeSignednessDataType() { - return isSigned() ? UnsignedCharDataType.dataType : SignedCharDataType.dataType; + public CharDataType getOppositeSignednessDataType() { + return isSigned() ? UnsignedCharDataType.dataType.clone(getDataTypeManager()) + : SignedCharDataType.dataType.clone(getDataTypeManager()); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java index 7375bfab7d..6f396f9602 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java @@ -15,8 +15,6 @@ */ package ghidra.program.model.data; -import ghidra.util.exception.InvalidInputException; - /** * Interface for common methods in Structure and Union */ @@ -95,7 +93,8 @@ public interface Composite extends DataType { public abstract DataTypeComponent[] getComponents(); /** - * Adds a new datatype to the end of this composite. + * Adds a new datatype to the end of this composite. This is the preferred method + * to use for adding components to an aligned structure for fixed-length dataTypes. * @param dataType the datatype to add. * @return the DataTypeComponent created. * @throws IllegalArgumentException if the specified data type is not @@ -106,21 +105,24 @@ public interface Composite extends DataType { public DataTypeComponent add(DataType dataType); /** - * Adds a new datatype to the end of this composite. + * Adds a new datatype to the end of this composite. This is the preferred method + * to use for adding components to an aligned structure for dynamic dataTypes such as + * strings whose length must be specified. * @param dataType the datatype to add. * @param length the length to associate with the datatype. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @return the componentDataType created. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to be added to this composite data type. + * allowed to be added to this composite data type or an invalid length + * is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to add dt1 to dt2 since this would cause a cyclic dependency. */ public DataTypeComponent add(DataType dataType, int length); /** - * Adds a new datatype to the end of this composite. + * Adds a new datatype to the end of this composite. This is the preferred method + * to use for adding components to an aligned structure for fixed-length dataTypes. * @param dataType the datatype to add. * @param name the field name to associate with this component. * @param comment the comment to associate with this component. @@ -133,16 +135,34 @@ public interface Composite extends DataType { public DataTypeComponent add(DataType dataType, String name, String comment); /** - * Adds a new datatype to the end of this composite. + * Adds a new bitfield to the end of this composite. This method is intended + * to be used with aligned structures/unions only where the bitfield will be + * appropriately packed. The minimum storage storage byte size will be applied. + * It will not provide useful results within unaligned composites. + * @param baseDataType the bitfield base datatype (certain restrictions apply). + * @param bitSize the bitfield size in bits + * @param componentName the field name to associate with this component. + * @param comment the comment to associate with this component. + * @return the componentDataType created whose associated data type will + * be BitFieldDataType. + * @throws InvalidDataTypeException if the specified data type is + * not a valid base type for bitfields. + */ + public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName, + String comment) throws InvalidDataTypeException; + + /** + * Adds a new datatype to the end of this composite. This is the preferred method + * to use for adding components to an aligned structure for dynamic dataTypes such as + * strings whose length must be specified. * @param dataType the datatype to add. * @param length the length to associate with the datatype. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @param name the field name to associate with this component. * @param comment the comment to associate with this component. * @return the componentDataType created. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to be added to this composite data type. + * allowed to be added to this composite data type or an invalid length is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to add dt1 to dt2 since this would cause a cyclic dependency. */ @@ -159,6 +179,7 @@ public interface Composite extends DataType { * allowed to be inserted into this composite data type. * For example, suppose dt1 contains dt2. Therefore it is not valid * to insert dt1 to dt2 since this would cause a cyclic dependency. + * @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds */ public DataTypeComponent insert(int ordinal, DataType dataType); @@ -169,16 +190,55 @@ public interface Composite extends DataType { * @param ordinal the ordinal where the new datatype is to be inserted. * @param dataType the datatype to insert. * @param length the length to associate with the datatype. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @return the componentDataType created. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to be inserted into this composite data type. + * allowed to be inserted into this composite data type or an invalid + * length is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to insert dt1 to dt2 since this would cause a cyclic dependency. + * @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds */ public DataTypeComponent insert(int ordinal, DataType dataType, int length); + /** + * Inserts a new bitfield at the specified ordinal position in this composite. + * Within an aligned composites the specified byteWidth and bitOffset will be ignored + * while bitOffset is always ignored for unions. + * The component length will be computed based upon the specified parameters and will + * be reduced from byteWidth to its minimal size for the new component. + *

+ * For unaligned structures, a component shift will only occur if the bitfield placement + * conflicts with another component. If no conflict occurs, the bitfield will be placed + * at the specified location consuming any DEFAULT components as needed. When a conflict + * does occur a shift will be performed at the ordinal position based upon the specified + * byteWidth. When located onto existing bitfields they will be packed together + * provided they do not conflict, otherwise the conflict rule above applies. + *

+ * Supported packing for little-endian fills lsb first, whereas big-endian fills msb first. + * Insertion behavior may not work as expected if packing rules differ from this. + * @param ordinal the ordinal where the new datatype is to be inserted. + * @param byteWidth the storage unit width which contains the bitfield. Must be large + * enough to contain the specified bitSize and corresponding bitOffset. The actual + * component size used will be recomputed during insertion. + * @param bitOffset corresponds to the bitfield left-shift amount with the storage + * unit when viewed as big-endian. The final offset may be reduced based upon + * the minimal storage size determined during insertion. + * @param baseDataType the bitfield base datatype (certain restrictions apply). + * @param bitSize the bitfield size in bits + * @param componentName the field name to associate with this component. + * @param comment the comment to associate with this component. + * @return the componentDataType created whose associated data type will + * be BitFieldDataType. + * @throws InvalidDataTypeException if the specified data type is + * not a valid base type for bitfields. + * @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the + * current number of components. + */ + public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException, ArrayIndexOutOfBoundsException; + /** * Inserts a new datatype at the specified ordinal position in this composite. *
Note: For an aligned structure the ordinal position will get adjusted @@ -186,15 +246,16 @@ public interface Composite extends DataType { * @param ordinal the ordinal where the new datatype is to be inserted. * @param dataType the datatype to insert. * @param length the length to associate with the datatype. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @param name the field name to associate with this component. * @param comment the comment to associate with this component. * @return the componentDataType created. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to be inserted into this composite data type. + * allowed to be inserted into this composite data type or an invalid length + * is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to insert dt1 to dt2 since this would cause a cyclic dependency. + * @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds */ public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name, String comment); @@ -204,14 +265,18 @@ public interface Composite extends DataType { *
Note: For an aligned structure the delete will have no effect if the * ordinal position is a component that provides alignment padding. * @param ordinal the ordinal of the component to be deleted. + * @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds */ public void delete(int ordinal); /** * Deletes the components at the given ordinal positions. - *
Note: For an aligned structure the delete will have no effect if the + *
Note 1: For an aligned structure the delete will have no effect if the * ordinal position is a component that provides alignment padding. + *
Note 2: Removal of bitfields from an unaligned structure will + * not shift other components with vacated bytes reverting to undefined. * @param ordinals the ordinals of the component to be deleted. + * @throws ArrayIndexOutOfBoundsException if any specified component ordinal is out of bounds */ public void delete(int[] ordinals); @@ -271,10 +336,10 @@ public interface Composite extends DataType { * become an internally aligned data type. *
Note: If a component's data type has a specific external alignment, it will * override this value if necessary. - * @param packingValue the new packing value or 0NOT_PACKING. - * @throws InvalidInputException if the packingValue isn't valid. + * @param packingValue the new packing value or 0 for NOT_PACKING. + * A negative value will be treated the same as 0. */ - public void setPackingValue(int packingValue) throws InvalidInputException; + public void setPackingValue(int packingValue); /** * Get the external alignment (a minimum alignment) for this DataType. @@ -292,9 +357,9 @@ public interface Composite extends DataType { * of the alignment. Calling this method will cause the data type to * become an internally aligned data type. * @param minimumAlignment the external (minimum) alignment for this DataType. - * @throws InvalidInputException if the external alignment isn't valid. + * Any value less than 1 will revert to default alignment. */ - public void setMinimumAlignment(int minimumAlignment) throws InvalidInputException; + public void setMinimumAlignment(int minimumAlignment); /** * Sets this data type's external (minimum) alignment to the default alignment. This data type's @@ -326,4 +391,13 @@ public interface Composite extends DataType { */ public boolean isMachineAligned(); + /** + * Get the bitfield packing information associated with the underlying + * data organization. + * @return bitfield packing information + */ + public default BitFieldPacking getBitFieldPacking() { + return getDataOrganization().getBitFieldPacking(); + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java new file mode 100644 index 0000000000..8ebb3c82e6 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java @@ -0,0 +1,143 @@ +/* ### + * 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.program.model.data; + +public class CompositeAlignmentHelper { + + private static int getImpartedAlignment(DataOrganization dataOrganization, int packingAlignment, + DataTypeComponent dataTypeComponent) { + + // FIXME: try to eliminate this method. + +// DataType componentDt = dataTypeComponent.getDataType(); +// +// if (componentDt instanceof BitFieldDataType) { +// BitFieldPacking bitFieldPacking = dataOrganization.getBitFieldPacking(); +// if (!bitFieldPacking.isTypeAlignmentEnabled() || +// isBitFieldPackingEnabled(dataOrganization, packingAlignment)) { +// return 0; +// } +// BitFieldDataType bitFieldDt = (BitFieldDataType) componentDt; +// // zero-length bitfield assumed not to influence composite alignment, only component alignment +// if (!bitFieldPacking.zeroLengthAffectsContainerAlignment() && +// bitFieldDt.getBitSize() == 0) { +// return 0; +// } +// // largest bit-field base-type will provide composite alignment constraint +// return getPackedAlignment(dataOrganization, packingAlignment, +// bitFieldDt.getBaseDataType(), bitFieldDt.getBaseTypeSize()); +// } + + return CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packingAlignment, + dataTypeComponent); + } + + private static int getCompositeAlignmentMultiple(DataOrganization dataOrganization, + Composite composite) { + int allComponentsLCM = 1; + int packingAlignment = composite.getPackingValue(); + + DataTypeComponent[] dataTypeComponents = composite.getComponents(); + for (DataTypeComponent dataTypeComponent : dataTypeComponents) { + int impartedAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, + packingAlignment, dataTypeComponent); + if (impartedAlignment != 0) { + allComponentsLCM = DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, + impartedAlignment); + } + } + if (composite instanceof Structure) { + Structure struct = (Structure) composite; + DataTypeComponent flexibleArrayComponent = struct.getFlexibleArrayComponent(); + if (flexibleArrayComponent != null) { + allComponentsLCM = getComponentAlignmentLCM(dataOrganization, allComponentsLCM, + packingAlignment, flexibleArrayComponent); + } + } + return allComponentsLCM; + } + + private static int getComponentAlignmentLCM(DataOrganization dataOrganization, + int allComponentsLCM, int packingValue, DataTypeComponent component) { + int componentAlignment = getPackedAlignment(dataOrganization, packingValue, component); + allComponentsLCM = + DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, componentAlignment); + return allComponentsLCM; + } + + public static int getPackedAlignment(DataOrganization dataOrganization, int packingValue, + DataTypeComponent component) { + + if (component.isZeroBitFieldComponent() && (component.getParent() instanceof Union) && + !dataOrganization.getBitFieldPacking().useMSConvention()) { + // Zero-length bitfields ignored within unions for non-MSVC cases + return 0; + } + + DataType componentDt = component.getDataType(); + int dtSize = componentDt.getLength(); + if (dtSize <= 0) { + dtSize = component.getLength(); + } + return getPackedAlignment(dataOrganization, packingValue, componentDt, dtSize); + } + + private static int getPackedAlignment(int componentAlignment, int forcedAlignment, + int packingAlignment) { + // Only do packing if we are not forcing an alignment. + int alignment = componentAlignment; + if (packingAlignment != Composite.NOT_PACKING) { // TODO Should this be packingValue > 0? + if (forcedAlignment > packingAlignment) { + alignment = forcedAlignment; + } + else if (alignment > packingAlignment) { + alignment = packingAlignment; + } + } + return alignment; + } + + public static int getPackedAlignment(DataOrganization dataOrganization, int packingAlignment, + DataType componentDt, int dtSize) { + int componentAlignment = dataOrganization.getAlignment(componentDt, dtSize); + int componentForcedAlignment = dataOrganization.getForcedAlignment(componentDt); + boolean componentForcingAlignment = componentForcedAlignment > 0; + if (componentForcingAlignment) { + componentAlignment = DataOrganizationImpl.getLeastCommonMultiple(componentAlignment, + componentForcedAlignment); + } + return getPackedAlignment(componentAlignment, componentForcedAlignment, packingAlignment); + } + + public static int getAlignment(DataOrganization dataOrganization, Composite dataType) { + + // TODO: goal is to eliminate this method in favor of pack once and remember alignment + + if (!dataType.isInternallyAligned()) { + return 1; // Unaligned + } + + int lcm = getCompositeAlignmentMultiple(dataOrganization, dataType); + int minimumAlignment = dataType.getMinimumAlignment(); + if ((minimumAlignment != Composite.DEFAULT_ALIGNMENT_VALUE) && + (lcm % minimumAlignment != 0)) { + lcm = DataOrganizationImpl.getLeastCommonMultiple(lcm, minimumAlignment); + } + int absoluteMaxAlignment = dataOrganization.getAbsoluteMaxAlignment(); + return ((absoluteMaxAlignment == 0) || (lcm < absoluteMaxAlignment)) ? lcm + : absoluteMaxAlignment; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java index 18ab67213a..d384a99823 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java @@ -21,6 +21,7 @@ import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.mem.MemBuffer; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; +import ghidra.util.exception.AssertException; import ghidra.util.exception.NotYetImplementedException; /** @@ -34,7 +35,7 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C // subtle errors - One I know of is in the StructureDataType // copyComponent method. It has built in assumptions about this. - protected AlignmentType currentAlignment = AlignmentType.DEFAULT_ALIGNED; + protected AlignmentType alignmentType = AlignmentType.DEFAULT_ALIGNED; protected int packingValue = NOT_PACKING; protected int externalAlignment = DEFAULT_ALIGNMENT_VALUE; @@ -58,6 +59,34 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C description = ""; } + /** + * Get the preferred length for a new component. For Unions and internally aligned + * structures the preferred component length for a fixed-length dataType will be the + * length of that dataType. Otherwise the length returned will be no larger then the + * specified length. + * @param dataType new component datatype + * @param length constrained length or -1 to force use of dataType size. Dynamic types + * such as string must have a positive length specified. + * @return preferred component length + */ + protected int getPreferredComponentLength(DataType dataType, int length) { + if ((isInternallyAligned() || (this instanceof Union)) && !(dataType instanceof Dynamic)) { + length = -1; // force use of datatype size + } + int dtLength = dataType.getLength(); + if (length <= 0) { + length = dtLength; + } + else if (dtLength > 0 && dtLength < length) { + length = dtLength; + } + if (length <= 0) { + throw new IllegalArgumentException("Positive length must be specified for " + + dataType.getDisplayName() + " component"); + } + return length; + } + @Override public boolean isDynamicallySized() { return isInternallyAligned(); @@ -77,6 +106,7 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C * data type. */ protected void checkAncestry(DataType dataType) { + // TODO: cyclic checks are easily bypassed by renaming multiple composite instances if (this.equals(dataType)) { throw new IllegalArgumentException( "Data type " + getDisplayName() + " can't contain itself."); @@ -94,6 +124,10 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C * @throws IllegalArgumentException if the data type is invalid. */ protected void validateDataType(DataType dataType) { + if (isInternallyAligned() && dataType == DataType.DEFAULT) { + throw new IllegalArgumentException( + "The DEFAULT data type is not allowed in an aligned composite data type."); + } if (dataType instanceof FactoryDataType) { throw new IllegalArgumentException("The \"" + dataType.getName() + "\" data type is not allowed in a composite data type."); @@ -108,9 +142,56 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C } @Override - public DataTypeComponent add(DataType dataType) { - dataType = dataType.clone(getDataTypeManager()); - return add(dataType, dataType.getLength(), null, null); + public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName, + String comment) throws InvalidDataTypeException { + + BitFieldDataType.checkBaseDataType(baseDataType); + baseDataType = baseDataType.clone(getDataTypeManager()); + + BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize); + return add(bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); + } + + /** + * Handle replacement of datatype which may impact bitfield datatype. + * @param bitfieldComponent bitfield component + * @param oldDt affected datatype which has been removed or replaced + * @param newDt replacement datatype + * @param true if bitfield component was modified + * @throws InvalidDataTypeException if new datatype is not + */ + protected boolean updateBitFieldDataType(DataTypeComponentImpl bitfieldComponent, + DataType oldDt, DataType newDt) throws InvalidDataTypeException { + if (!bitfieldComponent.isBitFieldComponent()) { + throw new AssertException("expected bitfield component"); + } + + BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldComponent.getDataType(); + if (bitfieldDt.getBaseDataType() != oldDt) { + return false; + } + + if (newDt != null) { + BitFieldDataType.checkBaseDataType(newDt); + int maxBitSize = 8 * newDt.getLength(); + if (bitfieldDt.getBitSize() > maxBitSize) { + throw new InvalidDataTypeException("Replacement datatype too small for bitfield"); + } + } + + try { + BitFieldDataType newBitfieldDt = + new BitFieldDataType(newDt, bitfieldDt.getDeclaredBitSize(), + bitfieldDt.getBitOffset(), bitfieldDt.getStorageSize()); + bitfieldComponent.setDataType(newBitfieldDt); + oldDt.removeParent(this); + newDt.addParent(this); + } + catch (InvalidDataTypeException e) { + throw new AssertException("unexpected"); + } + + return true; } @Override @@ -133,23 +214,28 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C } @Override - public DataTypeComponent add(DataType dataType, int length) { + public final DataTypeComponent add(DataType dataType) { + return add(dataType, -1, null, null); + } + + @Override + public final DataTypeComponent add(DataType dataType, int length) { return add(dataType, length, null, null); } @Override - public DataTypeComponent add(DataType dataType, String fieldName, String comment) { - return add(dataType, dataType.getLength(), fieldName, comment); + public final DataTypeComponent add(DataType dataType, String fieldName, String comment) { + return add(dataType, -1, fieldName, comment); } @Override - public DataTypeComponent insert(int ordinal, DataType dataType, int length) { + public final DataTypeComponent insert(int ordinal, DataType dataType, int length) { return insert(ordinal, dataType, length, null, null); } @Override - public DataTypeComponent insert(int ordinal, DataType dataType) { - return insert(ordinal, dataType, dataType.getLength(), null, null); + public final DataTypeComponent insert(int ordinal, DataType dataType) { + return insert(ordinal, dataType, -1, null, null); } @Override @@ -170,6 +256,9 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public void setPackingValue(int packingValue) { + if (packingValue < 0) { + packingValue = NOT_PACKING; + } aligned = true; this.packingValue = packingValue; adjustInternalAlignment(); @@ -177,19 +266,33 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public int getMinimumAlignment() { + if (alignmentType == AlignmentType.MACHINE_ALIGNED) { + return getMachineAlignment(); + } + if (alignmentType == AlignmentType.DEFAULT_ALIGNED) { + return Composite.DEFAULT_ALIGNMENT_VALUE; + } return externalAlignment; } @Override public void setMinimumAlignment(int externalAlignment) { - aligned = true; - if (currentAlignment != AlignmentType.ALIGNED_BY_VALUE) { - currentAlignment = AlignmentType.ALIGNED_BY_VALUE; + if (externalAlignment < 1) { + this.externalAlignment = DEFAULT_ALIGNMENT_VALUE; + alignmentType = AlignmentType.DEFAULT_ALIGNED; } - this.externalAlignment = externalAlignment; + else { + this.externalAlignment = externalAlignment; + alignmentType = AlignmentType.ALIGNED_BY_VALUE; + } + aligned = true; adjustInternalAlignment(); } + private int getMachineAlignment() { + return getDataOrganization().getMachineAlignment(); + } + @Override public boolean isInternallyAligned() { return aligned; @@ -197,12 +300,12 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public boolean isDefaultAligned() { - return currentAlignment == AlignmentType.DEFAULT_ALIGNED; + return alignmentType == AlignmentType.DEFAULT_ALIGNED; } @Override public boolean isMachineAligned() { - return currentAlignment == AlignmentType.MACHINE_ALIGNED; + return alignmentType == AlignmentType.MACHINE_ALIGNED; } @Override @@ -210,7 +313,7 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C if (this.aligned != aligned) { this.aligned = aligned; if (!aligned) { - currentAlignment = AlignmentType.DEFAULT_ALIGNED; + alignmentType = AlignmentType.DEFAULT_ALIGNED; packingValue = Composite.NOT_PACKING; } } @@ -220,14 +323,14 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public void setToDefaultAlignment() { aligned = true; - currentAlignment = AlignmentType.DEFAULT_ALIGNED; + alignmentType = AlignmentType.DEFAULT_ALIGNED; adjustInternalAlignment(); } @Override public void setToMachineAlignment() { aligned = true; - currentAlignment = AlignmentType.MACHINE_ALIGNED; + alignmentType = AlignmentType.MACHINE_ALIGNED; adjustInternalAlignment(); } @@ -254,23 +357,23 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public int getAlignment() { - return getDataOrganization().getAlignment(this, getLength()); + return CompositeAlignmentHelper.getAlignment(getDataOrganization(), this); } // set my alignment info to the same as the given composite - protected void setDataAlignmentInfo(Composite composite) { + protected void setAlignment(Composite composite) { aligned = composite.isInternallyAligned(); if (composite.isDefaultAligned()) { - currentAlignment = AlignmentType.DEFAULT_ALIGNED; + alignmentType = AlignmentType.DEFAULT_ALIGNED; } else if (composite.isMachineAligned()) { - currentAlignment = AlignmentType.MACHINE_ALIGNED; + alignmentType = AlignmentType.MACHINE_ALIGNED; } else { - if (currentAlignment != AlignmentType.ALIGNED_BY_VALUE) { - currentAlignment = AlignmentType.ALIGNED_BY_VALUE; + if (alignmentType != AlignmentType.ALIGNED_BY_VALUE) { + alignmentType = AlignmentType.ALIGNED_BY_VALUE; } externalAlignment = composite.getMinimumAlignment(); } @@ -288,7 +391,14 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C protected void dumpComponents(StringBuilder buffer, String pad) { for (DataTypeComponent dtc : getComponents()) { DataType dataType = dtc.getDataType(); + buffer.append(pad + dtc.getOffset()); buffer.append(pad + dataType.getDisplayName()); + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bfDt = (BitFieldDataType) dataType; + buffer.append("("); + buffer.append(Integer.toString(bfDt.getBitOffset())); + buffer.append(")"); + } buffer.append(pad + dtc.getLength()); buffer.append(pad + dtc.getFieldName()); String comment = dtc.getComment(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java index 17cd7d33a5..e404ccfd44 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +33,12 @@ public class DWordDataType extends AbstractIntegerDataType { super("dword", false, dtm); } + @Override public String getDescription() { return "Unsigned Double-Word (ddw, 4-bytes)"; } + @Override public int getLength() { return 4; } @@ -48,11 +49,12 @@ public class DWordDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return SignedDWordDataType.dataType; + public SignedDWordDataType getOppositeSignednessDataType() { + return SignedDWordDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public DWordDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java index bd7cd6361b..f99045fb88 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java @@ -38,7 +38,7 @@ public interface DataOrganization { * @return the left shift amount for shifted-pointers. */ int getPointerShift(); - + /** * @return true if the "char" type is signed */ @@ -126,6 +126,12 @@ public interface DataOrganization { */ int getSizeAlignment(int size) throws NoValueException; + /** + * Get the composite bitfield packing information associated with this data organization. + * @return composite bitfield packing information + */ + BitFieldPacking getBitFieldPacking(); + /** * Remove all entries from the size alignment map */ @@ -154,10 +160,11 @@ public interface DataOrganization { String getIntegerCTypeApproximation(int size, boolean signed); /** - * Determines the alignment value for the indicated data type. (i.e. how the dat type gets - * aligned within other data types.) + * Determines the alignment value for the indicated data type. (i.e. how the data type gets + * aligned within other data types.) NOTE: the alignment of bitfields is dependent upon packing + * rules which must be considered at the composite level. * @param dataType the data type - * @param dtSize the data type's size + * @param dtSize the data type's size or component size * @return the alignment */ int getAlignment(DataType dataType, int dtSize); @@ -174,5 +181,4 @@ public interface DataOrganization { * @return the aligned offset for the data type */ int getAlignmentOffset(int minimumOffset, DataType dataType, int dtSize); - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java index adb4165ff9..d68fad0f92 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java @@ -15,7 +15,7 @@ */ package ghidra.program.model.data; -import java.util.*; +import java.util.Arrays; import ghidra.program.model.lang.Language; import ghidra.util.datastruct.IntIntHashtable; @@ -48,7 +48,7 @@ public class DataOrganizationImpl implements DataOrganization { private boolean bigEndian = false; private boolean isSignedChar = true; - private String[] callingConventionNames; + private BitFieldPacking bitFieldPacking = new BitFieldPackingImpl(); /* * Map for determining the alignment of a data type based upon its size. @@ -82,59 +82,25 @@ public class DataOrganizationImpl implements DataOrganization { dataOrganization.setPointerSize(language.getDefaultSpace().getPointerSize()); dataOrganization.setBigEndian(language.isBigEndian()); } - - // Create list of acceptable calling conventions (include all generic conventions by default) - GenericCallingConvention[] genericCallingConventions = GenericCallingConvention.values(); - List names = new ArrayList<>(); - for (GenericCallingConvention convention : genericCallingConventions) { - if (convention != GenericCallingConvention.unknown) { - names.add(convention.name()); - } - } - Collections.sort(names); - dataOrganization.callingConventionNames = new String[names.size()]; - names.toArray(dataOrganization.callingConventionNames); - return dataOrganization; } /** - * Creates a new default DataOrganization with an empty size to alignment mapping which - * defines the alignment of a data type based on its size. + * Creates a new default DataOrganization with an empty size to alignment mapping. */ private DataOrganizationImpl() { } - /** - * Set data endianess - * @param bigEndian - */ - public void setBigEndian(boolean bigEndian) { - this.bigEndian = bigEndian; - } - - /** - * @return true if data stored big-endian byte order - */ @Override public boolean isBigEndian() { return bigEndian; } - /** - * @return the size of a pointer data type. - */ @Override public int getPointerSize() { return pointerSize; } - /** - * Shift amount affects interpretation of in-memory pointer values only - * and will also be reflected within instruction pcode. A value of zero indicates - * that shifted-pointers are not supported. - * @return the left shift amount for shifted-pointers. - */ @Override public int getPointerShift() { return pointerShift; @@ -145,78 +111,64 @@ public class DataOrganizationImpl implements DataOrganization { return isSignedChar; } - /** - * @return the size of a char (char) primitive data type. - */ @Override public int getCharSize() { return charSize; } - /** - * @return the size of a wide-char (wchar_t) primitive data type. - */ @Override public int getWideCharSize() { return wideCharSize; } - /** - * @return the size of a short primitive data type. - */ @Override public int getShortSize() { return shortSize; } - /** - * @return the size of a int primitive data type. - */ @Override public int getIntegerSize() { return integerSize; } - /** - * @return the size of a long primitive data type. - */ @Override public int getLongSize() { return longSize; } - /** - * @return the size of a long long primitive data type. - */ @Override public int getLongLongSize() { return longLongSize; } - /** - * @return the size of a float primitive data type. - */ @Override public int getFloatSize() { return floatSize; } - /** - * @return the size of a double primitive data type. - */ @Override public int getDoubleSize() { return doubleSize; } - /** - * @return the size of a long double primitive data type. - */ @Override public int getLongDoubleSize() { return longDoubleSize; } + @Override + public BitFieldPacking getBitFieldPacking() { + return bitFieldPacking; + } + + /** + * Set data endianess + * @param bigEndian + */ + public void setBigEndian(boolean bigEndian) { + this.bigEndian = bigEndian; + } + /** * Defines the size of a pointer data type. * @param shortSize the size of a short. @@ -425,8 +377,6 @@ public class DataOrganizationImpl implements DataOrganization { this.defaultPointerAlignment = defaultPointerAlignment; } - // ALIGNMENTS BASED ON SIZE - /** * Gets the alignment that is defined for a data type of the indicated size if one is defined. * @param size the size of the data type @@ -439,7 +389,7 @@ public class DataOrganizationImpl implements DataOrganization { } /** - * Gets the alignment that is defined for a data type of the indicated size if one is defined. + * Sets the alignment that is defined for a data type of the indicated size if one is defined. * @param size the size of the data type * @param alignment the alignment of the data type. */ @@ -447,6 +397,14 @@ public class DataOrganizationImpl implements DataOrganization { sizeAlignmentMap.put(size, alignment); } + /** + * Set the bitfield packing information associated with this data organization. + * @param bitFieldPacking bitfield packing information + */ + public void setBitFieldPacking(BitFieldPacking bitFieldPacking) { + this.bitFieldPacking = bitFieldPacking; + } + /** * Remove all entries from the size alignment map */ @@ -504,13 +462,6 @@ public class DataOrganizationImpl implements DataOrganization { return ctype; } - /** - * Determines the alignment value for the indicated data type. (i.e. how the dat type gets - * aligned within other data types.) - * @param dataType the data type - * @param dtSize the data type's size - * @return the alignment - */ @Override public int getAlignment(DataType dataType, int dtSize) { // Don't do alignment on dynamic data types. @@ -538,7 +489,14 @@ public class DataOrganizationImpl implements DataOrganization { // Structure's or Union's alignment is a multiple of the least common multiple of // the components. It can also be adjusted by packing and alignment attributes. if (dataType instanceof Composite) { - return getAlignment((Composite) dataType); + // IMPORTANT: composites are now responsible for computing their own alignment !! + return ((Composite) dataType).getAlignment(); + } + // Bit field alignment must be determined within the context of the containing structure. + // See AlignedStructurePacker. + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bitfieldDt = (BitFieldDataType) dataType; + return getAlignment(bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize()); } // Otherwise get the alignment based on the size. if (sizeAlignmentMap.contains(dtSize)) { @@ -583,6 +541,7 @@ public class DataOrganizationImpl implements DataOrganization { if (dataType instanceof Pointer) { return 0; } + // Structure's or Union's alignment is a multiple of the least common multiple of // the components. It can also be adjusted by packing and alignment attributes. if (dataType instanceof Composite) { @@ -607,6 +566,9 @@ public class DataOrganizationImpl implements DataOrganization { dataTypeComponents = composite.getComponents(); } for (DataTypeComponent dataTypeComponent : dataTypeComponents) { + if (dataTypeComponent.isBitFieldComponent()) { + continue; + } DataType componentDt = dataTypeComponent.getDataType(); int forcedAlignment = getForcedAlignment(componentDt); if (forcedAlignment > 0) { @@ -635,66 +597,6 @@ public class DataOrganizationImpl implements DataOrganization { return 0; } - private int getComponentAlignmentMultiple(Composite dataType) { - int allComponentsLCM = 1; - int packingValue = dataType.getPackingValue(); - DataTypeComponent[] dataTypeComponents = dataType.getComponents(); - for (DataTypeComponent dataTypeComponent : dataTypeComponents) { - allComponentsLCM = - getComponentAlignmentLCM(allComponentsLCM, packingValue, dataTypeComponent); - } - if (dataType instanceof Structure) { - Structure struct = (Structure) dataType; - DataTypeComponent flexibleArrayComponent = struct.getFlexibleArrayComponent(); - if (flexibleArrayComponent != null) { - allComponentsLCM = getComponentAlignmentLCM(allComponentsLCM, packingValue, - flexibleArrayComponent); - } - } - return allComponentsLCM; - } - - private int getComponentAlignmentLCM(int allComponentsLCM, int packingValue, - DataTypeComponent component) { - DataType componentDt = component.getDataType(); - int dtSize = componentDt.getLength(); - if (dtSize <= 0) { - dtSize = component.getLength(); - } - int componentAlignment = getAlignment(componentDt, dtSize); - int componentForcedAlignment = getForcedAlignment(componentDt); - boolean componentForcingAlignment = componentForcedAlignment > 0; - if (componentForcingAlignment) { - componentAlignment = - getLeastCommonMultiple(componentAlignment, componentForcedAlignment); - } - // Only do packing if we are not forcing an alignment. - if (packingValue != Composite.NOT_PACKING) { // TODO Should this be packingValue > 0? - if (componentForcedAlignment > packingValue) { - componentAlignment = componentForcedAlignment; - } - else if (componentAlignment > packingValue) { - componentAlignment = packingValue; - } - } - allComponentsLCM = getLeastCommonMultiple(allComponentsLCM, componentAlignment); - return allComponentsLCM; - } - - private int getAlignment(Composite dataType) { - if (!dataType.isInternallyAligned()) { - return 1; // Unaligned - } - int lcm = getComponentAlignmentMultiple(dataType); - int minimumAlignment = dataType.getMinimumAlignment(); - if ((minimumAlignment != Composite.DEFAULT_ALIGNMENT_VALUE) && - (lcm % minimumAlignment != 0)) { - lcm = getLeastCommonMultiple(lcm, minimumAlignment); - } - return ((absoluteMaxAlignment == 0) || (lcm < absoluteMaxAlignment)) ? lcm - : absoluteMaxAlignment; - } - /** * Determines the offset where the specified data type should be placed to be properly aligned. * @param minimumOffset the minimum allowable offset where the data type can be placed. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java index 41ff96547c..f91357a919 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java @@ -418,4 +418,9 @@ public interface DataType { */ public void setLastChangeTimeInSourceArchive(long lastChangeTimeInSourceArchive); + /** + * Returns the DataOrganization associated with this data-type + */ + public DataOrganization getDataOrganization(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java index 710a6b4610..a633336eb5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; * and Unions) dataTypes. *

* While most all components must have a fixed length greater than 0, structures support an - * optional trailing flexible array component whose length is zero and whose offset equals + * optional trailing flexible array component whose length is zero and whose offset equals * the length of the structure. */ public interface DataTypeComponent { @@ -37,7 +37,7 @@ public interface DataTypeComponent { /** * Returns the dataType in this component. *

- * NOTE: If this component corresponds to a structure flexible array the returned data type + * NOTE: If this component corresponds to a structure flexible array the returned data type * reflects the base type of the array (e.g., char is returned for a flexible char array). * @return the dataType in this component */ @@ -51,18 +51,30 @@ public interface DataTypeComponent { /** * Determine if this component corresponds to a unsized flexible array which is - * permitted as the trailing component within a structure. + * permitted as the trailing component within a structure. * @return true if component is a trailing flexible array component. */ public boolean isFlexibleArrayComponent(); + /** + * Determine if the specified component corresponds to a bit-field. + * @return true if bit-field else false + */ + public boolean isBitFieldComponent(); + + /** + * Determine if the specified component corresponds to a zero-length bit-field. + * @return true if zer-length bit-field else false + */ + public boolean isZeroBitFieldComponent(); + /** * Get the ordinal position within the parent dataType. *

* NOTE: The special case of a structure flexible array component returns an ordinal equal * to the parent structure's {@link Structure#getNumComponents()} since it is not included - * in the list of normal components (see {@link Structure#getFlexibleArrayComponent()}. - * @return ordinal of this component within the parent data type. + * in the list of normal components (see {@link Structure#getFlexibleArrayComponent()}. + * @return ordinal of this component within the parent data type. */ public int getOrdinal(); @@ -80,19 +92,19 @@ public interface DataTypeComponent { /** * Get the byte offset of where this component ends relative to the start of the parent - * data type. + * data type. *

- * NOTE: The special case of a structure flexible array component returns -1 since its + * NOTE: The special case of a structure flexible array component returns -1 since its * length is undefined. * @return offset of end of component relative to the start of the parent - * data type. + * data type. */ public int getEndOffset(); /** * Get the length of this component. *

- * NOTE: The special case of a structure flexible array component returns 0 since its + * NOTE: The special case of a structure flexible array component returns 0 since its * length is undefined. * @return the length of this component or 0 for a structure flexible array. */ @@ -153,4 +165,5 @@ public interface DataTypeComponent { * @return true if the given dataTypeComponent is equivalent to this dataTypeComponent. */ public boolean isEquivalent(DataTypeComponent dtc); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java index 388f7eb322..2b04e40c54 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Basic implementation of a DataTypeComponent */ -public class DataTypeComponentImpl implements DataTypeComponent, Serializable { +public class DataTypeComponentImpl implements InternalDataTypeComponent, Serializable { private final static long serialVersionUID = 1; private DataType dataType; @@ -53,17 +53,21 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { public DataTypeComponentImpl(DataType dataType, CompositeDataTypeImpl parent, int length, int ordinal, int offset, String fieldName, String comment) { - this.dataType = dataType; this.parent = parent; this.ordinal = ordinal; this.offset = offset; this.length = length; this.fieldName = fieldName; this.comment = comment; + setDataType(dataType); initFlexibleArrayComponent(); } private void initFlexibleArrayComponent() { + if (dataType instanceof BitFieldDataType || dataType instanceof Dynamic || + dataType instanceof FactoryDataType) { + return; + } isFlexibleArrayComponent = length == 0 && offset < 0 && ordinal < 0 && (parent instanceof Structure); } @@ -86,6 +90,20 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { return isFlexibleArrayComponent; } + @Override + public boolean isBitFieldComponent() { + return dataType instanceof BitFieldDataType; + } + + @Override + public boolean isZeroBitFieldComponent() { + if (isBitFieldComponent()) { + BitFieldDataType bitField = (BitFieldDataType) getDataType(); + return bitField.getBitSize() == 0; + } + return false; + } + @Override public int getOffset() { if (isFlexibleArrayComponent) { @@ -98,6 +116,13 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { return offset; } + boolean containsOffset(int off) { + if (isFlexibleArrayComponent) { + return false; + } + return off >= offset && off <= (offset + length - 1); + } + @Override public int getEndOffset() { return offset + length - 1; @@ -115,11 +140,17 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { @Override public String getFieldName() { + if (isZeroBitFieldComponent()) { + return ""; + } return fieldName; } @Override public String getDefaultFieldName() { + if (isZeroBitFieldComponent()) { + return ""; + } if (parent instanceof Structure) { return DEFAULT_FIELD_NAME_PREFIX + "_0x" + Integer.toHexString(getOffset()); } @@ -186,6 +217,13 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { return parent; } + @Override + public void update(int ordinal, int offset, int length) { + this.ordinal = ordinal; + this.offset = offset; + this.length = length; + } + /** * Set the byte offset of where this component begins in its immediate parent * data type. @@ -248,10 +286,13 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { DataTypeComponent dtc = (DataTypeComponent) obj; DataType myDt = getDataType(); DataType otherDt = dtc.getDataType(); - if (offset != dtc.getOffset() || length != dtc.getLength() || ordinal != dtc.getOrdinal() || + + // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from + // stored values for flexible array component + if (getOffset() != dtc.getOffset() || getLength() != dtc.getLength() || + getOrdinal() != dtc.getOrdinal() || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { - return false; } if (!(myDt instanceof Pointer)) { @@ -285,25 +326,48 @@ public class DataTypeComponentImpl implements DataTypeComponent, Serializable { public boolean isEquivalent(DataTypeComponent dtc) { DataType myDt = getDataType(); DataType otherDt = dtc.getDataType(); - int otherLength = dtc.getLength(); DataType myParent = getParent(); boolean aligned = (myParent instanceof Composite) ? ((Composite) myParent).isInternallyAligned() : false; // Components don't need to have matching offset when they are aligned, only matching ordinal. if ((!aligned && (getOffset() != dtc.getOffset())) || // Components don't need to have matching length when they are aligned. Is this correct? - (!aligned && (getLength() != otherLength)) || getOrdinal() != dtc.getOrdinal() || + // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from + // stored values for flexible array component + (!aligned && (getLength() != dtc.getLength())) || getOrdinal() != dtc.getOrdinal() || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { - return false; } return DataTypeUtilities.isSameOrEquivalentDataType(myDt, otherDt); } - void setDataType(DataType dt) { + @Override + public void setDataType(DataType dt) { dataType = dt; + if (dt instanceof BitFieldDataType) { + // bit-field packing may change component size + setLength(dt.getLength()); + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(" " + ordinal); + buffer.append(" " + offset); + buffer.append(" " + dataType.getDisplayName()); + if (isFlexibleArrayComponent) { + buffer.append("[ ]"); + } + else if (dataType instanceof BitFieldDataType) { + buffer.append("(" + ((BitFieldDataType) dataType).getBitOffset() + ")"); + } + buffer.append(" " + length); + buffer.append(" " + fieldName); + buffer.append(" " + ((comment != null) ? ("\"" + comment + "\"") : comment)); + return buffer.toString(); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeDependencyException.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeDependencyException.java index c68a88db8f..54d32f4360 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeDependencyException.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeDependencyException.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +16,16 @@ package ghidra.program.model.data; /** - * Exception thrown when trying to replace a dataType with a dataType that depends - * on the dataType being replaced. For example try to replace byte with byte[] fail - * because byte[] depends on byte. + * DataTypeDependencyException corresponds to a datatype dependency failure. + * This can occur under various situations, including when trying to replace a dataType + * with a dataType that depends on the dataType being replaced. This error may also occur + * when a datatype dependency can not be satisfied. */ public class DataTypeDependencyException extends Exception { public DataTypeDependencyException() { super(); - // TODO Auto-generated constructor stub } public DataTypeDependencyException(String message) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java index 4741b3914b..afaab7746b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java @@ -15,8 +15,6 @@ */ package ghidra.program.model.data; -import java.io.Serializable; -import java.net.URL; import java.util.ArrayList; import java.util.Iterator; @@ -25,23 +23,20 @@ import javax.swing.event.ChangeListener; import ghidra.app.plugin.core.datamgr.archive.SourceArchive; import ghidra.docking.settings.*; -import ghidra.program.model.mem.MemBuffer; import ghidra.util.*; /** * Base implementation for dataTypes. */ -public abstract class DataTypeImpl implements DataType, ChangeListener, Serializable { +public abstract class DataTypeImpl extends AbstractDataType implements ChangeListener { + private final static SettingsDefinition[] EMPTY_DEFINITIONS = new SettingsDefinition[0]; protected Settings defaultSettings; - protected String name; - protected CategoryPath categoryPath; private ArrayList parentList; private UniversalID universalID; private SourceArchive sourceArchive; private long lastChangeTime; private long lastChangeTimeInSourceArchive; - private DataTypeManager dataMgr; protected DataTypeImpl(CategoryPath path, String name, DataTypeManager dataMgr) { this(path, name, null, null, System.currentTimeMillis(), NO_LAST_CHANGE_TIME, dataMgr); @@ -50,20 +45,7 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ DataTypeImpl(CategoryPath path, String name, UniversalID universalID, SourceArchive sourceArchive, long lastChangeTime, long lastChangeTimeInSourceArchive, DataTypeManager dataMgr) { - if (path == null) { - throw new IllegalArgumentException("Category Path is null!"); - } - if (name == null || name.length() == 0) { - throw new IllegalArgumentException("Name is null or empty!"); - } - - // allow spaces since derived types may have spaces (pointers for example: foo *32) - if (!DataUtilities.isValidDataTypeName(name)) { - throw new IllegalArgumentException("Invalid DataType name: " + name); - } - this.categoryPath = path; - this.name = name; - this.dataMgr = dataMgr; + super(path, name, dataMgr); defaultSettings = new SettingsImpl(this, null); parentList = new ArrayList<>(); this.universalID = universalID == null ? UniversalIdGenerator.nextID() : universalID; @@ -72,94 +54,26 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ this.lastChangeTimeInSourceArchive = lastChangeTimeInSourceArchive; } - /** - * Returns the DataOrganization associated with this - * data-types DataTypeManager - */ - protected final DataOrganization getDataOrganization() { - DataOrganization dataOrganization = null; - if (dataMgr != null) { - dataOrganization = dataMgr.getDataOrganization(); - } - if (dataOrganization == null) { - dataOrganization = DataOrganizationImpl.getDefaultOrganization(); - } - return dataOrganization; - } - - @Override - public boolean isNotYetDefined() { - return false; - } - @Override public Class getValueClass(Settings settings) { return null; } - /** - * @see ghidra.program.model.data.DataType#getDefaultSettings() - */ @Override public Settings getDefaultSettings() { return defaultSettings; } - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#getCategoryPath() - */ - @Override - public CategoryPath getCategoryPath() { - return categoryPath; - } - - @Override - public DataTypePath getDataTypePath() { - return new DataTypePath(categoryPath, name); - } - - /** - * @see ghidra.program.model.data.DataType#getDocs() - */ - @Override - public URL getDocs() { - return null; - } - - /** - * @see ghidra.program.model.data.DataType#getSettingsDefinitions() - */ @Override public SettingsDefinition[] getSettingsDefinitions() { return EMPTY_DEFINITIONS; } - /** - * @see ghidra.program.model.data.DataType#getName() - */ - @Override - public String getName() { - return name; - } - - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#getDisplayName() - */ - @Override - public String getDisplayName() { - return getName(); - } - - @Override - public String toString() { - return getDisplayName(); - } - /** * Check if the name is a valid name for a data type - * + * * @param checkedName name to check - * + * * @throws InvalidNameException if the name is invalid */ void checkValidName(String checkedName) throws InvalidNameException { @@ -168,42 +82,16 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ } } - /** - * @see ghidra.program.model.data.DataType#isDeleted() - */ - @Override - public boolean isDeleted() { - return false; - } - - /** - * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent) - */ @Override public void stateChanged(ChangeEvent e) { // don't care } - /** - * @see ghidra.program.model.data.DataType#getDataTypeManager() - */ - @Override - public DataTypeManager getDataTypeManager() { - return dataMgr; - } - - /** - * - * @see ghidra.program.model.data.DataType#setDefaultSettings(ghidra.docking.settings.Settings) - */ @Override public void setDefaultSettings(Settings settings) { defaultSettings = settings; } - /** - * @see ghidra.program.model.data.DataType#getPathName() - */ @Override public String getPathName() { return getDataTypePath().getPath(); @@ -218,25 +106,16 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ return getDataOrganization().getAlignment(this, length); } - /** - * @see ghidra.program.model.data.DataType#addParent(ghidra.program.model.data.DataType) - */ @Override public void addParent(DataType dt) { parentList.add(dt); } - /** - * @see ghidra.program.model.data.DataType#removeParent(ghidra.program.model.data.DataType) - */ @Override public void removeParent(DataType dt) { parentList.remove(dt); } - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#getParents() - */ @Override public DataType[] getParents() { DataType[] dts = new DataType[parentList.size()]; @@ -257,7 +136,7 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ /** * Notify any parents that my name has changed. - * + * * @param oldName */ protected void notifyNameChanged(String oldName) { @@ -291,29 +170,6 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ } } - @Override - public String getDefaultLabelPrefix() { - return null; - } - - @Override - public String getDefaultAbbreviatedLabelPrefix() { - return getDefaultLabelPrefix(); - } - - @Override - public String getDefaultLabelPrefix(MemBuffer buf, Settings settings, int len, - DataTypeDisplayOptions options) { - return getDefaultLabelPrefix(); - } - - @Override - public String getDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings, int len, - DataTypeDisplayOptions options, int offcutLength) { - // By default we will do nothing different for offcut values - return getDefaultLabelPrefix(buf, settings, len, options); - } - @Override public long getLastChangeTime() { return lastChangeTime; @@ -370,7 +226,7 @@ public abstract class DataTypeImpl implements DataType, ChangeListener, Serializ @Override public int hashCode() { // Note: this works because the DTMs have to be equal and there can be only one DT with - // the same name and category path + // the same name and category path return getName().hashCode(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java index 86e72c729a..99d47c17c7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java @@ -484,7 +484,8 @@ public interface DataTypeManager { public boolean updateSourceArchiveName(UniversalID sourceID, String name); /** - * Get the data organization associated with this data type manager + * Get the data organization associated with this data type manager. Note that the + * DataOrganization settings may not be changed dynamically. * @return data organization (will never be null) */ public DataOrganization getDataOrganization(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java index 693aa4355e..7750b12c56 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java @@ -40,6 +40,7 @@ public class DefaultDataType extends DataTypeImpl { * * @see ghidra.program.model.data.DataType#getMnemonic(Settings) */ + @Override public String getMnemonic(Settings settings) { return "??"; } @@ -48,6 +49,7 @@ public class DefaultDataType extends DataTypeImpl { * * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return 1; } @@ -55,6 +57,7 @@ public class DefaultDataType extends DataTypeImpl { /** * @see ghidra.program.model.data.DataType#isDynamicallySized() */ + @Override public boolean isDynamicallySized() { return false; } @@ -63,6 +66,7 @@ public class DefaultDataType extends DataTypeImpl { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Undefined Byte"; } @@ -71,6 +75,7 @@ public class DefaultDataType extends DataTypeImpl { * * @see ghidra.program.model.data.DataType#getRepresentation(MemBuffer, Settings, int) */ + @Override public String getRepresentation(MemBuffer buf, Settings settings, int length) { try { int b = buf.getByte(0) & 0xff; @@ -96,6 +101,7 @@ public class DefaultDataType extends DataTypeImpl { * @param length the number of bytes to get the value from. * @return the data Object. */ + @Override public Object getValue(MemBuffer buf, Settings settings, int length) { try { return new Scalar(8, buf.getByte(0)); @@ -105,10 +111,12 @@ public class DefaultDataType extends DataTypeImpl { } } + @Override public DataType clone(DataTypeManager dtm) { return this; } + @Override public DataType copy(DataTypeManager dtm) { return this; } @@ -116,12 +124,14 @@ public class DefaultDataType extends DataTypeImpl { /** * @see ghidra.program.model.data.DataType#dataTypeSizeChanged(ghidra.program.model.data.DataType) */ + @Override public void dataTypeSizeChanged(DataType dt) { } /** * @see ghidra.program.model.data.DataType#isEquivalent(ghidra.program.model.data.DataType) */ + @Override public boolean isEquivalent(DataType dt) { return dt == this; } @@ -129,43 +139,50 @@ public class DefaultDataType extends DataTypeImpl { /** * @see ghidra.program.model.data.DataType#setCategoryPath(ghidra.program.model.data.CategoryPath) */ + @Override public void setCategoryPath(CategoryPath path) throws DuplicateNameException { } /** * @see ghidra.program.model.data.DataType#setName(java.lang.String) */ - public void setName(String name) throws InvalidNameException, DuplicateNameException { + @Override + public void setName(String name) { } /** * @see ghidra.program.model.data.DataType#setNameAndCategory(ghidra.program.model.data.CategoryPath, java.lang.String) */ - public void setNameAndCategory(CategoryPath path, String name) throws InvalidNameException, - DuplicateNameException { + @Override + public void setNameAndCategory(CategoryPath path, String name) + throws InvalidNameException, DuplicateNameException { } /** * @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType) */ + @Override public void dataTypeDeleted(DataType dt) { } /** * @see ghidra.program.model.data.DataType#dataTypeNameChanged(ghidra.program.model.data.DataType, java.lang.String) */ + @Override public void dataTypeNameChanged(DataType dt, String oldName) { } /** * @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType) */ + @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { } /** * @see ghidra.program.model.data.DataType#dependsOn(ghidra.program.model.data.DataType) */ + @Override public boolean dependsOn(DataType dt) { return false; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java index b11f6538ab..98f0b69b89 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java @@ -15,8 +15,11 @@ */ package ghidra.program.model.data; +import java.math.BigInteger; import java.util.NoSuchElementException; +import ghidra.docking.settings.Settings; + public interface Enum extends DataType { /** @@ -69,4 +72,12 @@ public interface Enum extends DataType { */ @Override public void setDescription(String description); + + /** + * Get enum representation of the big-endian value. + * @param bigInt BigInteger value with the appropriate sign + * @param settings integer format settings (PADDING, FORMAT, etc.) + * @return formatted integer string + */ + public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java index 22a0d225fd..c18320a892 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java @@ -15,6 +15,7 @@ */ package ghidra.program.model.data; +import java.math.BigInteger; import java.util.*; import ghidra.app.plugin.core.datamgr.archive.SourceArchive; @@ -267,17 +268,26 @@ public class EnumDataType extends GenericDataType implements Enum { value = buf.getLong(0); break; } - String valueName = getName(value); - if (valueName == null) { - valueName = getCompoundValue(value); - } - return valueName; + return getRepresentation(value); } catch (MemoryAccessException e) { return "??"; } } + @Override + public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength) { + return getRepresentation(bigInt.longValue()); + } + + private String getRepresentation(long value) { + String valueName = getName(value); + if (valueName == null) { + valueName = getCompoundValue(value); + } + return valueName; + } + private String getCompoundValue(long value) { if (value == 0) { return "0"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java index 2a980d4bd3..132959c615 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java @@ -33,20 +33,23 @@ public class Integer16DataType extends AbstractIntegerDataType { super("int16", true, dtm); } + @Override public String getDescription() { return "Signed 16-Byte Integer"; } + @Override public int getLength() { return 16; } @Override - public DataType getOppositeSignednessDataType() { - return UnsignedInteger16DataType.dataType; + public UnsignedInteger16DataType getOppositeSignednessDataType() { + return UnsignedInteger16DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public Integer16DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java index a52754fb86..36b682d5b2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class Integer3DataType extends AbstractIntegerDataType { super("int3", true, dtm); } + @Override public String getDescription() { return "Signed 3-Byte Integer"; } + @Override public int getLength() { return 3; } @Override - public DataType getOppositeSignednessDataType() { - return UnsignedInteger3DataType.dataType; + public UnsignedInteger3DataType getOppositeSignednessDataType() { + return UnsignedInteger3DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public Integer3DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java index be3787e884..8be233505a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class Integer5DataType extends AbstractIntegerDataType { super("int5", true, dtm); } + @Override public String getDescription() { return "Signed 5-Byte Integer"; } + @Override public int getLength() { return 5; } @Override - public DataType getOppositeSignednessDataType() { - return UnsignedInteger5DataType.dataType; + public UnsignedInteger5DataType getOppositeSignednessDataType() { + return UnsignedInteger5DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public Integer5DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java index ea7721db91..fe4733b0af 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class Integer6DataType extends AbstractIntegerDataType { super("int6", true, dtm); } + @Override public String getDescription() { return "Signed 6-Byte Integer"; } + @Override public int getLength() { return 6; } @Override - public DataType getOppositeSignednessDataType() { - return UnsignedInteger6DataType.dataType; + public UnsignedInteger6DataType getOppositeSignednessDataType() { + return UnsignedInteger6DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public Integer6DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java index 1e71a4a17a..135a74f0c5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class Integer7DataType extends AbstractIntegerDataType { super("int7", true, dtm); } + @Override public String getDescription() { return "Signed 7-Byte Integer"; } + @Override public int getLength() { return 7; } @Override - public DataType getOppositeSignednessDataType() { - return UnsignedInteger7DataType.dataType; + public UnsignedInteger7DataType getOppositeSignednessDataType() { + return UnsignedInteger7DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public Integer7DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java index 40a55bd18b..1e5afa62a6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +44,7 @@ public class IntegerDataType extends AbstractIntegerDataType { /** * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getIntegerSize(); } @@ -53,6 +53,7 @@ public class IntegerDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Signed Integer (compiler-specific size)"; } @@ -63,11 +64,12 @@ public class IntegerDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public UnsignedIntegerDataType getOppositeSignednessDataType() { return UnsignedIntegerDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public IntegerDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java new file mode 100644 index 0000000000..f21897737b --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java @@ -0,0 +1,34 @@ +/* ### + * 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.program.model.data; + +public interface InternalDataTypeComponent extends DataTypeComponent { + + /** + * Sets the DataType for this component + * @param dataType the new DataType for this component + */ + public void setDataType(DataType dataType); + + /** + * Update component ordinal, offset and length during alignment + * @param ordinal + * @param offset + * @param length + */ + void update(int ordinal, int offset, int length); + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java index f0f3ed2fd9..3dfc62419b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +36,7 @@ public class LongDataType extends AbstractIntegerDataType { /** * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getLongSize(); } @@ -53,6 +53,7 @@ public class LongDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Signed Long Integer (compiler-specific size)"; } @@ -63,11 +64,12 @@ public class LongDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public UnsignedLongDataType getOppositeSignednessDataType() { return UnsignedLongDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public LongDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java index aa36ed41d8..d62997b162 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +36,7 @@ public class LongLongDataType extends AbstractIntegerDataType { /** * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getLongLongSize(); } @@ -53,6 +53,7 @@ public class LongLongDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Signed Long Long Integer (compiler-specific size)"; } @@ -63,11 +64,12 @@ public class LongLongDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public UnsignedLongLongDataType getOppositeSignednessDataType() { return UnsignedLongLongDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public LongLongDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MissingBuiltInDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MissingBuiltInDataType.java index 8d25947354..b695ab69a9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MissingBuiltInDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MissingBuiltInDataType.java @@ -72,6 +72,7 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /* (non-Javadoc) * @see ghidra.program.model.data.DataType#getMnemonic(ghidra.program.model.data.Settings) */ + @Override public String getMnemonic(Settings settings) { return getName(); } @@ -79,6 +80,7 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /* (non-Javadoc) * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return -1; } @@ -86,10 +88,12 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /** * @see ghidra.program.model.data.DataType#isDynamicallySized() */ + @Override public boolean isDynamicallySized() { return true; } + @Override public boolean canSpecifyLength() { return true; } @@ -97,6 +101,7 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /** * @see ghidra.program.model.data.Dynamic#getLength(ghidra.program.model.mem.MemBuffer, int) */ + @Override public int getLength(MemBuffer buf, int maxLength) { return -1; } @@ -104,6 +109,7 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /* (non-Javadoc) * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Missing Built-In Data Type: " + missingBuiltInClassPath; } @@ -111,6 +117,7 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /* (non-Javadoc) * @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.program.model.lang.ProcessorContext, ghidra.program.model.data.Settings, int) */ + @Override public String getRepresentation(MemBuffer buf, Settings settings, int length) { return missingBuiltInClassPath; } @@ -118,41 +125,50 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /* (non-Javadoc) * @see ghidra.program.model.data.DataType#getValue(ghidra.program.model.mem.MemBuffer, ghidra.program.model.lang.ProcessorContext, ghidra.program.model.data.Settings, int) */ + @Override public Object getValue(MemBuffer buf, Settings settings, int length) { return missingBuiltInClassPath; } + @Override public DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } - return new MissingBuiltInDataType(categoryPath, missingBuiltInName, - missingBuiltInClassPath, dtm); + return new MissingBuiltInDataType(categoryPath, missingBuiltInName, missingBuiltInClassPath, + dtm); } /** * @see ghidra.program.model.data.DataType#copy(ghidra.program.model.data.DataTypeManager) */ + @Override public final DataType copy(DataTypeManager dtm) { return clone(dtm); } + @Override public void dataTypeDeleted(DataType dt) { } + @Override public void dataTypeNameChanged(DataType dt, String oldName) { } + @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { } + @Override public void dataTypeSizeChanged(DataType dt) { } + @Override public boolean dependsOn(DataType dt) { return false; } + @Override public boolean isEquivalent(DataType dt) { if (dt == null) { return false; @@ -163,17 +179,21 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { if (!(dt instanceof MissingBuiltInDataType)) { return false; } - return missingBuiltInClassPath.equals(((MissingBuiltInDataType) dt).missingBuiltInClassPath); + return missingBuiltInClassPath.equals( + ((MissingBuiltInDataType) dt).missingBuiltInClassPath); } + @Override public void setCategoryPath(CategoryPath path) throws DuplicateNameException { } - public void setName(String name) throws InvalidNameException, DuplicateNameException { + @Override + public void setName(String name) throws InvalidNameException { } - public void setNameAndCategory(CategoryPath path, String name) throws InvalidNameException, - DuplicateNameException { + @Override + public void setNameAndCategory(CategoryPath path, String name) + throws InvalidNameException, DuplicateNameException { } @Override @@ -184,10 +204,12 @@ public class MissingBuiltInDataType extends DataTypeImpl implements Dynamic { /** * @see ghidra.program.model.data.BuiltInDataType#getCTypeDeclaration(ghidra.program.model.data.DataOrganization) */ + @Override public String getCTypeDeclaration(DataOrganization dataOrganization) { return null; // missing type } + @Override public DataType getReplacementBaseType() { return null; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java index c1ce8750eb..de85f83a9a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +33,12 @@ public class QWordDataType extends AbstractIntegerDataType { super("qword", false, dtm); } + @Override public String getDescription() { return "Unsigned Quad-Word (dq, 8-bytes)"; } + @Override public int getLength() { return 8; } @@ -48,11 +49,12 @@ public class QWordDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return SignedQWordDataType.dataType; + public SignedQWordDataType getOppositeSignednessDataType() { + return SignedQWordDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public QWordDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java index e05e0da4c6..ad0e66a975 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java @@ -78,6 +78,19 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl return false; // Unsupported use } + @Override + public boolean isBitFieldComponent() { + return dataType instanceof BitFieldDataType; + } + + @Override + public boolean isZeroBitFieldComponent() { + if (dataType instanceof BitFieldDataType) { + return ((BitFieldDataType) dataType).getBitSize() == 0; + } + return false; + } + /** * @see ghidra.program.model.data.DataTypeComponent#getOffset() */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java index ddb4a9c572..e17d62ee18 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +36,7 @@ public class ShortDataType extends AbstractIntegerDataType { /** * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getShortSize(); } @@ -53,6 +53,7 @@ public class ShortDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Signed Short Integer (compiler-specific size)"; } @@ -63,11 +64,12 @@ public class ShortDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public UnsignedShortDataType getOppositeSignednessDataType() { return UnsignedShortDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public ShortDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java index b7edffdfd1..50b96dd33b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java @@ -35,10 +35,12 @@ public class SignedByteDataType extends AbstractIntegerDataType { super("sbyte", true, dtm); } + @Override public String getDescription() { return "Signed Byte (sdb)"; } + @Override public int getLength() { return 1; } @@ -56,11 +58,12 @@ public class SignedByteDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return ByteDataType.dataType; + public ByteDataType getOppositeSignednessDataType() { + return ByteDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public SignedByteDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java index 06c55bc742..e79ef3bcf3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java @@ -43,7 +43,10 @@ public class SignedCharDataType extends CharDataType { } @Override - public DataType clone(DataTypeManager dtm) { + public SignedCharDataType clone(DataTypeManager dtm) { + if (dtm == getDataTypeManager()) { + return this; + } return new SignedCharDataType(dtm); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java index 4b30668ada..b1e5e1a3de 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +33,12 @@ public class SignedDWordDataType extends AbstractIntegerDataType { super("sdword", true, dtm); } + @Override public String getDescription() { return "Signed Double-Word (sddw, 4-bytes)"; } + @Override public int getLength() { return 4; } @@ -48,11 +49,12 @@ public class SignedDWordDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return DWordDataType.dataType; + public DWordDataType getOppositeSignednessDataType() { + return DWordDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public SignedDWordDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java index 3416bb79b8..1a2800f0f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +33,12 @@ public class SignedQWordDataType extends AbstractIntegerDataType { super("sqword", true, dtm); } + @Override public String getDescription() { return "Signed Quad-Word (sdq, 8-bytes)"; } + @Override public int getLength() { return 8; } @@ -48,11 +49,12 @@ public class SignedQWordDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return QWordDataType.dataType; + public QWordDataType getOppositeSignednessDataType() { + return QWordDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public SignedQWordDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java index 14f74c9cdd..7e909ce939 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +33,12 @@ public class SignedWordDataType extends AbstractIntegerDataType { super("sword", true, dtm); } + @Override public String getDescription() { return "Signed Word (sdw, 2-bytes)"; } + @Override public int getLength() { return 2; } @@ -48,11 +49,12 @@ public class SignedWordDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return WordDataType.dataType; + public WordDataType getOppositeSignednessDataType() { + return WordDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public SignedWordDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java index 6f95cb5f2a..5374120938 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java @@ -15,14 +15,20 @@ */ package ghidra.program.model.data; +import java.util.Comparator; + import ghidra.util.exception.InvalidInputException; /** * The structure interface. - * + *

* NOTE: Structures containing only a flexible array will report a length of 1 * which will result in improper code unit sizing since we are unable to support a * defined data of length 0. + *

+ * NOTE: The use of zero-length bitfields within unaligned structures is discouraged since + * they have no real affect and are easily misplaced. Their use should be reserved for + * aligned/packed structures. */ public interface Structure extends Composite { @@ -41,7 +47,9 @@ public interface Structure extends Composite { /** * Gets the immediate child component that contains the byte - * at the given offset. + * at the given offset. If the specified offset corresponds to + * a bit-field,the first bit-field component containing the offset + * will be returned. * @param offset the byte offset into this data type * @return the immediate child component. */ @@ -50,71 +58,71 @@ public interface Structure extends Composite { /** * Returns the primitive Data Type that is at this offset. This is useful * for prototypes that have components that are made up of other components + * If the specified offset corresponds to + * a bit-field,the BitFieldDataType of the first bit-field component containing + * the offset will be returned. * @param offset the byte offset into this data type. * @return the primitive data type at the offset. */ public abstract DataTypeComponent getDataTypeAt(int offset); /** - * - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType) + * Inserts a new bitfield at the specified location in this composite. + * This method is intended to be used with unaligned structures where + * the bitfield will be precisely placed. Within an aligned structure the specified + * byteOffset, byteWidth and bitOffset will be used to identify the appropriate ordinal + * but may not be preserved. The component length will be computed + * based upon the specified parameters and will be reduced from byteWidth to + * its minimal size for the new component. + *

+ * For unaligned mode, a component shift will only occur if the bitfield placement + * conflicts with another component. If no conflict occurs, the bitfield will be placed + * at the specified location consuming any DEFAULT components as needed. When a conflict + * does occur a shift will be performed at the point of conflict based upon the specified + * byteWidth. When located onto existing bitfields they will be packed together + * provided they do not conflict, otherwise the conflict rule above applies. + *

+ * Supported packing for little-endian fills lsb first, whereas big-endian fills msb first. + * Insertion behavior may not work as expected if packing rules differ from this. + *

+ * Zero length bitfields may be inserted although they have no real affect for + * unaligned structures. Only the resulting byte offset within the structure + * is of significance in determining its' ordinal placement. + *

+ * @param byteOffset the first byte offset within this structure which corresponds to the + * first byte of the specified storage unit identified by its byteWidth. + * @param byteWidth the storage unit width which contains the bitfield. Must be large + * enough to contain the specified bitSize and corresponding bitOffset. The actual + * component size used will be recomputed during insertion. + * @param bitOffset corresponds to the bitfield left-shift amount with the storage + * unit when viewed as big-endian. The final offset may be reduced based upon + * the minimal storage size determined during insertion. + * @param baseDataType the bitfield base datatype (certain restrictions apply). + * @param componentName the field name to associate with this component. + * @param bitSize the bitfield size in bits. A bitSize of 0 may be specified + * although its name will be ignored. + * @param comment the comment to associate with this component. + * @return the componentDataType created whose associated data type will + * be BitFieldDataType. + * @throws InvalidDataTypeException if the specified data type is + * not a valid base type for bitfields. */ - @Override - public DataTypeComponent add(DataType dataType); + public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException; /** - * - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType, int) - */ - @Override - public DataTypeComponent add(DataType dataType, int length); - - /** - * - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ - @Override - public DataTypeComponent add(DataType dataType, int length, String name, String comment); - - /** - * - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType) - */ - @Override - public DataTypeComponent insert(int index, DataType dataType); - - /** - * - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType, int) - */ - @Override - public DataTypeComponent insert(int index, DataType dataType, int length); - - /** - * - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ - @Override - public DataTypeComponent insert(int index, DataType dataType, int length, String name, - String comment); - - public void insert(int ordinal, DataType dataType, int length, String name, String comment, - int numCopies); - - /** - * Deletes the ComponentDataType at the given index - * @param index the index of the component to be deleted. - */ - @Override - public void delete(int index); - - /** - * Inserts a new datatype at the specified offset into this structure. + * Inserts a new datatype at the specified offset into this structure. + * Inserting a component will causing any conflicting component + * to shift down to the extent necessary to avoid a conflict. * @param offset the byte offset into the structure where the new datatype is to be inserted. * @param dataType the datatype to insert. + * @param length the length to associate with the dataType. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @return the componentDataType created. * @throws IllegalArgumentException if the specified data type is not - * allowed to be inserted into this composite data type. + * allowed to be inserted into this composite data type or an invalid length + * is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to insert dt1 to dt2 since this would cause a cyclic dependency. */ @@ -122,16 +130,17 @@ public interface Structure extends Composite { /** * Inserts a new datatype at the specified offset into this structure. + * Inserting a component will causing any conflicting component + * to shift down to the extent necessary to avoid a conflict. * @param offset the byte offset into the structure where the new datatype is to be inserted. * @param dataType the datatype to insert. - * @param length the length to associate with the dataType; + * @param length the length to associate with the dataType. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @param name the field name to associate with this component. * @param comment the comment to associate with this component. * @return the componentDataType created. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to be inserted into this composite data type. + * allowed to be inserted into this composite data type or an invalid length is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to insert dt1 to dt2 since this would cause a cyclic dependency. */ @@ -139,7 +148,9 @@ public interface Structure extends Composite { String comment); /** - * Deletes the datatype at the specified offset in this structure. + * Deletes the component containing the specified offset in this structure. If the offset + * corresponds to a bit-field, all bit-fields whose base type group contains the offset will + * be removed. * @param offset the byte offset into the structure where the datatype is to be deleted. */ public void deleteAtOffset(int offset); @@ -152,10 +163,11 @@ public interface Structure extends Composite { public void deleteAll(); /** - * clears the defined component at the given component index. Clearing a + * Clears the defined component at the given component index. Clearing a * component causes a defined component to be replaced with a number of * undefined dataTypes to offset the removal of the defined dataType. * @param index the index of the component to clear. + * @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds */ public void clearComponent(int index); @@ -164,13 +176,17 @@ public interface Structure extends Composite { * of the indicated data type. * @param index the index where the datatype is to be replaced. * @param dataType the datatype to insert. - * @param length the length of the dataType to insert + * @param length the length of the dataType to insert. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @return the new componentDataType at the index. * @throws IllegalArgumentException if the specified data type is not - * allowed to replace a component in this composite data type. + * allowed to replace a component in this composite data type or an invalid + * length is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to replace a dt2 component with dt1 since this would cause a cyclic - * dependency. + * dependency. In addition, any attempt to replace an existing bit-field + * component or specify a {@link BitFieldDatatype} will produce this error. + * @throws ArrayIndexOutOfBoundsException if component index is out of bounds */ public DataTypeComponent replace(int index, DataType dataType, int length); @@ -179,38 +195,44 @@ public interface Structure extends Composite { * of the indicated data type. * @param index the index where the datatype is to be replaced. * @param dataType the datatype to insert. - * @param length the length to associate with the dataType; + * @param length the length to associate with the dataType. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @param name the field name to associate with this component. * @param comment the comment to associate with this component. * @return the new componentDataType at the index. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to replace a component in this composite data type. + * allowed to replace a component in this composite data type or an invalid + * length is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to replace a dt2 component with dt1 since this would cause a cyclic - * dependency. + * dependency. In addition, any attempt to replace an existing bit-field + * component or specify a {@link BitFieldDatatype} will produce this error. + * @throws ArrayIndexOutOfBoundsException if component index is out of bounds */ public DataTypeComponent replace(int index, DataType dataType, int length, String name, String comment); /** * Replaces the component at the specified byte offset with a new component - * of the indicated data type. + * of the indicated data type. If the offset corresponds to a bit-field, all bit-fields + * at that offset will be removed and replaced by the specified component. Keep in mind + * bit-field or any component removal must clear sufficient space for an unaligned + * structure to complete the replacement. * @param offset the byte offset into the structure where the datatype is * to be replaced. * @param dataType the datatype to insert. - * @param length the length to associate with the dataType; + * @param length the length to associate with the dataType. + * For fixed length types a length <= 0 will use the length of the resolved dataType. * @param name the field name to associate with this component. * @param comment the comment to associate with this component. * @return the new componentDataType at the index. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not - * allowed to replace a component in this composite data type. + * allowed to replace a component in this composite data type or an invalid + * length is specified. * For example, suppose dt1 contains dt2. Therefore it is not valid * to replace a dt2 component with dt1 since this would cause a cyclic - * dependency. + * dependency. In addition, any attempt to replace an existing bit-field + * component or specify a {@link BitFieldDatatype} will produce this error. */ public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name, String comment); @@ -284,4 +306,128 @@ public interface Structure extends Composite { public void pack(int maxAlignment) throws InvalidInputException; + /** + * BitOffsetComparator provides ability to compare an Integer bit offset + * with a DataTypeComponent object. The offset will be consider equal (0) if + * the component contains the offset. Bit offsets for this comparator number the + * msb of the first byte in the structure as 0 and the lsb of the last byte as + * (8 * structLength) - 1. Due to the storage order of bitfields within little-endian + * organization a normalized bit-offset must be used which corresponds to the msb getting + * filled first (in reality little-endian fills the lsb first). + */ + public static class BitOffsetComparator implements Comparator { + + private boolean bigEndian; + + public BitOffsetComparator(boolean bigEndian) { + this.bigEndian = bigEndian; + } + + @Override + public int compare(Object o1, Object o2) { + if (o1 instanceof Integer) { + return -compare(o2, o1); + } + DataTypeComponent dtc = (DataTypeComponent) o1; + int bitOffset = ((Integer) o2).intValue(); + + int startBit, endBit; + if (dtc.isBitFieldComponent()) { + BitFieldDataType bitfield = (BitFieldDataType) dtc.getDataType(); + startBit = getNormalizedBitfieldOffset(dtc.getOffset(), dtc.getLength(), + bitfield.getBitSize(), bitfield.getBitOffset(), bigEndian); + endBit = startBit + bitfield.getBitSize() - 1; + } + else { + startBit = 8 * dtc.getOffset(); + endBit = startBit + (8 * dtc.getLength()) - 1; + } + + if (bitOffset < startBit) { + return 1; + } + if (bitOffset > endBit) { + return -1; + } + return 0; + } + + /** + * Compute the normalize bit offset of a bitfield relative to the start of a structure where + * the lsb of a bit-field is assigned an offset smaller than the msb. + * @param byteOffset byte offset within structure of storage unit + * @param storageSize storage unit size (i.e., component length) + * @param effectiveBitSize size of bitfield in bits + * @param bitOffset left shift amount for bitfield based upon a big-endian view of the + * storage unit + * @param bigEndian true if big-endian packing applies + * @return normalized bit-offset + */ + public static int getNormalizedBitfieldOffset(int byteOffset, int storageSize, + int effectiveBitSize, int bitOffset, boolean bigEndian) { + int offset = (8 * byteOffset); + if (effectiveBitSize == 0) { + // force zero-length bitfield placement + effectiveBitSize = 1; + if (bigEndian) { + bitOffset |= 7; + } + else { + bitOffset &= 0xfffffff8; + } + } + if (bigEndian) { + offset += (8 * storageSize) - effectiveBitSize - bitOffset; + } + else { + offset += bitOffset; + } + return offset; + } + + } + + /** + * OffsetComparator provides ability to compare an Integer offset + * with a DataTypeComponent object. The offset will be consider equal (0) if + * the component contains the offset. + */ + public static class OffsetComparator implements Comparator { + + @Override + public int compare(Object o1, Object o2) { + if (o1 instanceof Integer) { + return -compare(o2, o1); + } + DataTypeComponent dtc = (DataTypeComponent) o1; + int offset = ((Integer) o2).intValue(); + if (offset < dtc.getOffset()) { + return 1; + } + else if (offset > dtc.getEndOffset()) { + return -1; + } + return 0; + } + + } + + /** + * OrdinalComparator provides ability to compare an Integer ordinal + * with a DataTypeComponent object. The offset will be consider equal (0) if + * the component corresponds to the specified ordinal. + */ + public static class OrdinalComparator implements Comparator { + + @Override + public int compare(Object o1, Object o2) { + if (o1 instanceof Integer) { + return -compare(o2, o1); + } + DataTypeComponent dtc = (DataTypeComponent) o1; + int ordinal = ((Integer) o2).intValue(); + return dtc.getOrdinal() - ordinal; + } + + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java index 280e12824d..844e868fed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java @@ -19,7 +19,9 @@ import java.util.*; import ghidra.app.plugin.core.datamgr.archive.SourceArchive; import ghidra.docking.settings.Settings; +import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult; import ghidra.program.model.mem.MemBuffer; +import ghidra.util.Msg; import ghidra.util.UniversalID; import ghidra.util.exception.InvalidInputException; @@ -30,10 +32,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur private final static long serialVersionUID = 1; private static Comparator ordinalComparator = new OrdinalComparator(); protected static Comparator offsetComparator = new OffsetComparator(); + protected static Comparator bitOffsetComparatorLE = new BitOffsetComparator(false); + protected static Comparator bitOffsetComparatorBE = new BitOffsetComparator(true); protected int structLength; protected int numComponents; // excludes optional flexible array component protected List components; private DataTypeComponentImpl flexibleArrayComponent; + private int alignment = -1; /** * Construct a new structure with the given name and number of undefined bytes @@ -100,6 +105,18 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return structLength == 0 && flexibleArrayComponent == null; } + @Override + public int getAlignment() { + if (!isInternallyAligned()) { + return 1; // Unaligned + } + if (alignment <= 0) { + StructurePackResult packResult = AlignedStructureInspector.packComponents(this); + alignment = packResult.alignment; + } + return alignment; + } + @Override public DataTypeComponent getComponentAt(int offset) { if (offset >= structLength || offset < 0) { @@ -107,7 +124,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); if (index >= 0) { - return components.get(index); + DataTypeComponent dtc = components.get(index); + if (dtc.isBitFieldComponent()) { + index = backupToFirstComponentContainingOffset(index, offset); + dtc = components.get(index); + } + return dtc; } else if (isInternallyAligned()) { return null; @@ -142,39 +164,89 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public void delete(int index) { - if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + public void delete(int ordinal) { + if (ordinal < 0 || ordinal >= numComponents) { + throw new ArrayIndexOutOfBoundsException(ordinal); + } + int idx; + if (isInternallyAligned()) { + idx = ordinal; + } + else { + idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); } - int idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); if (idx >= 0) { - DataTypeComponent dtc = components.remove(idx); - shiftOffsets(idx, -1, -dtc.getLength()); + doDelete(idx); + adjustInternalAlignment(); + } + else { + // assume unaligned removal of DEFAULT + idx = -idx - 1; + shiftOffsets(idx, -1, -1); + } + notifySizeChanged(); + } + + private void doDelete(int index) { + DataTypeComponentImpl dtc = components.remove(index); + dtc.getDataType().removeParent(this); + if (isInternallyAligned()) { return; } - idx = -idx - 1; - shiftOffsets(idx, -1, -1); - adjustInternalAlignment(); - notifySizeChanged(); + int shiftAmount = 0; + // Bitfields: do not remove space previously occupied + if (!dtc.isBitFieldComponent()) { + shiftAmount = dtc.getLength(); + } + shiftOffsets(index, -1, -shiftAmount); } @Override public void delete(int[] ordinals) { + for (int ordinal : ordinals) { - delete(ordinal); + if (ordinal < 0 || ordinal >= numComponents) { + throw new ArrayIndexOutOfBoundsException(ordinal); + } } + + // delete ordinals in reverse order so that they remain valid + // during individual deletes + int[] sortedOrdinals = ordinals.clone(); + Arrays.sort(sortedOrdinals); + + for (int i = sortedOrdinals.length - 1; i >= 0; i--) { + int ordinal = sortedOrdinals[i]; + int idx; + if (isInternallyAligned()) { + idx = ordinal; + } + else { + idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); + } + if (idx >= 0) { + doDelete(idx); + } + else { + // assume unaligned removal of DEFAULT + idx = -idx - 1; + shiftOffsets(idx, -1, -1); + } + } + adjustInternalAlignment(); + notifySizeChanged(); } private void shiftOffsets(int index, int deltaOrdinal, int deltaOffset) { for (int i = index; i < components.size(); i++) { DataTypeComponentImpl dtc = components.get(i); - shiftOffsets(dtc, deltaOrdinal, deltaOffset); + shiftOffset(dtc, deltaOrdinal, deltaOffset); } structLength += deltaOffset; numComponents += deltaOrdinal; } - protected void shiftOffsets(DataTypeComponentImpl dtc, int deltaOrdinal, int deltaOffset) { + protected void shiftOffset(DataTypeComponentImpl dtc, int deltaOrdinal, int deltaOffset) { dtc.setOffset(dtc.getOffset() + deltaOffset); dtc.setOrdinal(dtc.getOrdinal() + deltaOrdinal); } @@ -191,6 +263,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (idx >= 0) { return components.get(idx); } + // assume unaligned DEFAULT int offset = 0; idx = -idx - 1; if (idx == 0) { @@ -215,22 +288,31 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, + public final DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length) { + return insertAtOffset(offset, dataType, length, null, null); + } + + @Override + public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length, String componentName, String comment) { if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } validateDataType(dataType); - if (offset > structLength) { + + dataType = dataType.clone(getDataTypeManager()); + checkAncestry(dataType); + + if ((offset > structLength) && !isInternallyAligned()) { numComponents = numComponents + (offset - structLength); structLength = offset; } - checkAncestry(dataType); int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); int additionalShift = 0; if (index >= 0) { + index = backupToFirstComponentContainingOffset(index, offset); DataTypeComponent dtc = components.get(index); additionalShift = offset - dtc.getOffset(); } @@ -245,17 +327,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } if (dataType == DataType.DEFAULT) { + // assume unaligned insert of DEFAULT shiftOffsets(index, 1 + additionalShift, 1 + additionalShift); return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); } - dataType = dataType.clone(getDataTypeManager()); - - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } + length = getPreferredComponentLength(dataType, length); DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal, offset, componentName, comment); @@ -267,58 +344,60 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return dtc; } - @Override - public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length) { - return insertAtOffset(offset, dataType, length, null, null); - } - @Override public DataTypeComponent add(DataType dataType, int length, String componentName, String comment) { - if (length < 1) { - throw new IllegalArgumentException("Length of " + componentName + ": '" + dataType + - "' in structure '" + this.name + "' must be >= 1!"); - } - return doAdd(dataType, length, componentName, comment, true, true); + return doAdd(dataType, length, false, componentName, comment); } - public void add(DataType dataType, int length, String componentName, String comment, - int numCopies) { - if (length < 1) { - throw new IllegalArgumentException("Length of " + componentName + ": '" + dataType + - "' in structure '" + this.name + "' must be >= 1!"); - } - for (int ii = 0; ii < numCopies; ++ii) { - doAdd(dataType, length, componentName, comment, false, false); - } - adjustInternalAlignment(); - notifySizeChanged(); - } + /** + * Add a new component to the end of this structure. + *

+ * NOTE: This method differs from inserting to the end the structure for the unaligned + * case in that this method will always grow the structure by the positive length + * specified while the insert may limit its growth by the length of a smaller fixed-length + * dataType. + * @param dataType component data type + * @param length maximum component length or -1 to use length of fixed-length dataType + * after applying structures data organization as determined by data type manager. + * If dataType is Dynamic, a positive length must be specified. + * @param isFlexibleArray if true length is ignored and the trailing flexible array will be + * set based upon the specified fixed-length dataType; + * @param componentName component name + * @param comment componetn comment + * @return + */ + private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray, + String componentName, String comment) { - private DataTypeComponent doAdd(DataType dataType, int length, String componentName, - String comment, boolean notify, boolean align) { validateDataType(dataType); + + dataType = dataType.clone(getDataTypeManager()); checkAncestry(dataType); DataTypeComponentImpl dtc; - boolean isFlexibleArray = false; if (dataType == DataType.DEFAULT) { dtc = new DataTypeComponentImpl(DataType.DEFAULT, this, 1, numComponents, structLength); } else { + int offset = structLength; int ordinal = numComponents; - isFlexibleArray = (length == 0); - if (length == 0) { + + int componentLength; + if (isFlexibleArray) { // assume trailing flexible array component offset = -1; ordinal = -1; - isFlexibleArray = true; clearFlexibleArrayComponent(); + componentLength = 0; } - dataType = dataType.clone(getDataTypeManager()); - dtc = new DataTypeComponentImpl(dataType, this, length, ordinal, offset, componentName, - comment); + else { + componentLength = getPreferredComponentLength(dataType, length); + } + + dtc = new DataTypeComponentImpl(dataType, this, componentLength, ordinal, offset, + componentName, comment); dataType.addParent(this); if (isFlexibleArray) { flexibleArrayComponent = dtc; @@ -328,15 +407,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } } if (!isFlexibleArray) { + + int structureGrowth = dtc.getLength(); + if (!isInternallyAligned() && length > 0) { + structureGrowth = length; + } + numComponents++; - structLength += dtc.getLength(); - } - if (align) { - adjustInternalAlignment(); - } - if (notify) { - notifySizeChanged(); + structLength += structureGrowth; } + adjustInternalAlignment(); + notifySizeChanged(); return dtc; } @@ -358,24 +439,27 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return add(dataType, length, componentName, comment); } validateDataType(dataType); + + dataType = dataType.clone(getDataTypeManager()); checkAncestry(dataType); - int idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); + int idx; + if (isInternallyAligned()) { + idx = index; + } + else { + idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); + } if (idx < 0) { idx = -idx - 1; } if (dataType == DataType.DEFAULT) { + // assume unaligned insert of DEFAULT shiftOffsets(idx, 1, 1); return getComponent(index); } - dataType = dataType.clone(getDataTypeManager()); - - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } + length = getPreferredComponentLength(dataType, length); int offset = (getComponent(index)).getOffset(); DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, index, offset, @@ -389,18 +473,194 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public void insert(int ordinal, DataType dataType, int length, String name, String comment, - int numCopies) { + public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { + if (ordinal < 0 || ordinal > numComponents) { throw new ArrayIndexOutOfBoundsException(ordinal); } - if (ordinal == numComponents) { - add(dataType, length, name, comment, numCopies); - return; + + BitFieldDataType.checkBaseDataType(baseDataType); + baseDataType = baseDataType.clone(getDataTypeManager()); + + if (!isInternallyAligned()) { + int offset = structLength; + if (ordinal < numComponents) { + offset = getComponent(ordinal).getOffset(); + } + return insertBitFieldAt(offset, byteWidth, bitOffset, baseDataType, bitSize, + componentName, comment); } - for (int ii = 0; ii < numCopies; ++ii) { - insert(ordinal + ii, dataType, length, name, comment); + + // handle aligned bitfield insertion + BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize); + return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); + } + + @Override + public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException { + + if (byteOffset < 0 || bitSize < 0) { + throw new IllegalArgumentException( + "Negative values not permitted when defining bitfield"); } + if (byteWidth <= 0) { + throw new IllegalArgumentException("Invalid byteWidth"); + } + + BitFieldDataType.checkBaseDataType(baseDataType); + baseDataType = baseDataType.clone(getDataTypeManager()); + + int effectiveBitSize = + BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength()); + + int minByteWidth = BitFieldDataType.getMinimumStorageSize(effectiveBitSize + bitOffset); + if (byteWidth < minByteWidth) { + throw new IllegalArgumentException( + "Bitfield does not fit within specified constraints"); + } + + boolean bigEndian = getDataOrganization().isBigEndian(); + + boolean hasConflict = false; + int additionalShift = 0; + + int startBitOffset = BitOffsetComparator.getNormalizedBitfieldOffset(byteOffset, byteWidth, + effectiveBitSize, bitOffset, bigEndian); + + Comparator bitOffsetComparator = + bigEndian ? bitOffsetComparatorBE : bitOffsetComparatorLE; + int startIndex = + Collections.binarySearch(components, new Integer(startBitOffset), bitOffsetComparator); + if (startIndex < 0) { + startIndex = -startIndex - 1; + } + else { + hasConflict = true; + DataTypeComponentImpl dtc = components.get(startIndex); + if (bitSize == 0 || dtc.isZeroBitFieldComponent()) { + hasConflict = dtc.getOffset() != (startBitOffset / 8); + } + if (hasConflict) { + additionalShift = byteOffset - dtc.getOffset(); + } + } + + int ordinal; // computed ordinal will be adjusted after insertion complete + if (startIndex < components.size()) { + DataTypeComponentImpl dtc = components.get(startIndex); + ordinal = dtc.getOrdinal(); + } + else { + ordinal = startIndex; + } + + if (isInternallyAligned()) { + insertBitField(ordinal, 0, 0, baseDataType, effectiveBitSize, componentName, comment); + } + + int endIndex = startIndex; + if (startIndex < components.size()) { + // some shifting of components may be required + int endBitOffset = startBitOffset + effectiveBitSize - 1; + endIndex = Collections.binarySearch(components, new Integer(endBitOffset), + bitOffsetComparator); + if (endIndex < 0) { + endIndex = -endIndex - 1; + } + else { + hasConflict = true; + } + } + + if (startIndex != endIndex) { + hasConflict = true; + } + + // Any conflict will force a full insertion of byteWidth + if (hasConflict) { + shiftOffsets(startIndex, 1, byteWidth + additionalShift); + } + + int requiredLength = byteOffset + byteWidth; + if (requiredLength > structLength) { + structLength = requiredLength; + } + + // use minimal storage + int storageBitOffset = bitOffset % 8; + int storageSize = + BitFieldDataType.getMinimumStorageSize(effectiveBitSize + storageBitOffset); + + int revisedOffset; + if (bigEndian) { + revisedOffset = byteOffset + byteWidth - ((effectiveBitSize + bitOffset + 7) / 8); + } + else { + revisedOffset = byteOffset + (bitOffset / 8); + } + + BitFieldDataType bitfieldDt = + new BitFieldDataType(baseDataType, bitSize, storageBitOffset, storageSize); + + DataTypeComponentImpl dtc = new DataTypeComponentImpl(bitfieldDt, this, storageSize, + ordinal, revisedOffset, componentName, comment); + bitfieldDt.addParent(this); // currently has no affect + + components.add(startIndex, dtc); + adjustUnalignedComponents(); + notifySizeChanged(); + return dtc; + } + + /** + * Backup from specified ordinal to the first component which contains + * the specified offset. For normal components the specified + * ordinal will be returned, however for bit-fields the ordinal of the first + * bit-field containing the specified offset will be returned. + * @param ordinal component ordinal + * @param offset offset within structure + * @return index of first defined component containing specific offset. + */ + private int backupToFirstComponentContainingOffset(int index, int offset) { + if (index == 0) { + return 0; + } + DataTypeComponentImpl dtc = components.get(index); + while (index != 0 && dtc.isBitFieldComponent()) { + DataTypeComponentImpl previous = components.get(index - 1); + if (!previous.containsOffset(offset)) { + break; + } + dtc = previous; + --index; + } + return index; + } + + /** + * Advance from specified ordinal to the last component which contains + * the specified offset. For normal components the specified + * ordinal will be returned, however for bit-fields the ordinal of the last + * bit-field containing the specified offset will be returned. + * @param ordinal component ordinal + * @param offset offset within structure + * @return index of last defined component containing specific offset. + */ + private int advanceToLastComponentContainingOffset(int index, int offset) { + DataTypeComponentImpl dtc = components.get(index); + while (index < (components.size() - 1) && dtc.isBitFieldComponent()) { + DataTypeComponentImpl next = components.get(index + 1); + if (!next.containsOffset(offset)) { + break; + } + dtc = next; + ++index; + } + return index; } @Override @@ -408,17 +668,31 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } + if (offset >= structLength) { + return; + } int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); - int delta = -1; + + int offsetDelta = 0; + int ordinalDelta = 0; if (index < 0) { index = -index - 1; + --ordinalDelta; + offsetDelta = -1; + shiftOffsets(index, ordinalDelta, offsetDelta); } else { - DataTypeComponent dtc = components.remove(index); - dtc.getDataType().removeParent(this); - delta = -dtc.getLength(); + index = advanceToLastComponentContainingOffset(index, offset); + DataTypeComponentImpl dtc = components.get(index); + while (dtc.containsOffset(offset)) { + doDelete(index); + if (--index < 0) { + break; + } + dtc = components.get(index); + } } - shiftOffsets(index, -1, delta); + adjustInternalAlignment(); notifySizeChanged(); return; @@ -475,69 +749,45 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public void dataTypeSizeChanged(DataType dt) { - // TODO FIXME - int n = components.size(); + if (isInternallyAligned()) { + adjustInternalAlignment(); + return; + } boolean didChange = false; + int n = components.size(); for (int i = 0; i < n; i++) { DataTypeComponentImpl dtc = components.get(i); + int nextIndex = i + 1; if (dtc.getDataType() == dt) { + // assume no impact to bitfields since base types + // should not change size int dtLen = dt.getLength(); int dtcLen = dtc.getLength(); if (dtLen < dtcLen) { dtc.setLength(dtLen); - shiftOffsets(i + 1, dtcLen - dtLen, 0); + shiftOffsets(nextIndex, dtcLen - dtLen, 0); didChange = true; } else if (dtLen > dtcLen) { int consumed = consumeBytesAfter(i, dtLen - dtcLen); if (consumed > 0) { - shiftOffsets(i + 1, 0 - consumed, 0); + shiftOffsets(nextIndex, 0 - consumed, 0); didChange = true; } } } } - adjustInternalAlignment(); if (didChange) { + adjustInternalAlignment(); notifySizeChanged(); } } @Override public void dataTypeAlignmentChanged(DataType dt) { - adjustInternalAlignment(); - // TODO FIXME -// checkDeleted(); -// if (!isShowingUndefinedBytes()) { -// adjustPacking(getDataOrganization(), false, true); -// return; -// } -// int n = components.size(); -// boolean didChange = false; -// for(int i=0;i dtcLen) { -// int consumed = consumeBytesAfter(i, dtLen-dtcLen); -// if (consumed > 0) { -// dtc.updateRecord(); -// shiftOffsets(i+1, -consumed, 0); -// didChange = true; -// } -// } -// } -// } -// if (didChange) { -// notifySizeChanged(); -// } + if (isInternallyAligned()) { + adjustInternalAlignment(); + } } /** @@ -621,21 +871,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * For example, suppose dt1 contains dt2. Therefore it is not valid * to replace a dt2 component with dt1 since this would cause a cyclic * dependency. - * @see ghidra.program.database.data.DataTypeDB#replaceWith(ghidra.program.model.data.DataType) */ @Override public void replaceWith(DataType dataType) { if (!(dataType instanceof Structure)) { throw new IllegalArgumentException(); } - int oldLength = structLength; - doReplaceWith((Structure) dataType); - if (oldLength != structLength) { - notifySizeChanged(); - } - } - private void doReplaceWith(Structure struct) { + Structure struct = (Structure) dataType; + + int oldLength = structLength; + components.clear(); flexibleArrayComponent = null; if (struct.isNotYetDefined()) { @@ -644,105 +890,198 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } else { structLength = struct.getLength(); - numComponents = structLength; + numComponents = isInternallyAligned() ? 0 : structLength; } - DataTypeComponent[] otherComponents = struct.getDefinedComponents(); - for (int i = 0; i < otherComponents.length; i++) { - DataTypeComponent dtc = otherComponents[i]; - DataType dt = dtc.getDataType(); - replaceAtOffset(dtc.getOffset(), dt, dtc.getLength(), dtc.getFieldName(), - dtc.getComment()); + + setAlignment(struct); + + if (struct.isInternallyAligned()) { + doReplaceWithAligned(struct); } - // ok now that all components have been laid down, see if we can make any of them bigger - // without affecting any offsets - for (int i = 0; i < components.size(); i++) { - DataTypeComponent dtc = components.get(i); - DataType dataType = dtc.getDataType(); - if (dataType.getLength() > dtc.getLength()) { - int n = consumeBytesAfter(i, dataType.getLength() - dtc.getLength()); - if (n > 0) { - shiftOffsets(i + 1, 0 - n, 0); - } - } + else { + doReplaceWithUnaligned(struct); } + DataTypeComponent flexComponent = struct.getFlexibleArrayComponent(); if (flexComponent != null) { setFlexibleArrayComponent(flexComponent.getDataType(), flexComponent.getFieldName(), flexComponent.getComment()); } - setDataAlignmentInfo(struct); + + if (oldLength != structLength) { + notifySizeChanged(); + } + } + + private void doReplaceWithAligned(Structure struct) { + // assumes components is clear and that alignment characteristics have been set + DataTypeComponent[] otherComponents = struct.getDefinedComponents(); + for (int i = 0; i < otherComponents.length; i++) { + DataTypeComponent dtc = otherComponents[i]; + DataType dt = dtc.getDataType(); + int length = (dt instanceof Dynamic) ? dtc.getLength() : -1; + add(dt, length, dtc.getFieldName(), dtc.getComment()); + } + } + + private void doReplaceWithUnaligned(Structure struct) { + // assumes components is clear and that alignment characteristics have been set. + + // NOTE: unaligned bitfields should remain unchanged when + // transitioning endianess even though it makes little sense. + // Unaligned structures are not intended to be portable! + + DataTypeComponent[] otherComponents = struct.getDefinedComponents(); + for (int i = 0; i < otherComponents.length; i++) { + DataTypeComponent dtc = otherComponents[i]; + DataType dt = dtc.getDataType().clone(dataMgr); + checkAncestry(dt); + + int length = getPreferredComponentLength(dt, dtc.getLength()); + + components.add(new DataTypeComponentImpl(dt, this, length, dtc.getOrdinal(), + dtc.getOffset(), dtc.getFieldName(), dtc.getComment())); + } + adjustComponents(); } @Override public void dataTypeDeleted(DataType dt) { + boolean didChange = false; + if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == dt) { + flexibleArrayComponent.getDataType().removeParent(this); + flexibleArrayComponent = null; + didChange = true; + } int n = components.size(); for (int i = n - 1; i >= 0; i--) { DataTypeComponentImpl dtc = components.get(i); - if (dtc.getDataType() == dt) { + boolean removeBitFieldComponent = false; + if (dtc.isBitFieldComponent()) { + BitFieldDataType bitfieldDt = (BitFieldDataType) dtc.getDataType(); + removeBitFieldComponent = bitfieldDt.getBaseDataType() == dt; + } + if (removeBitFieldComponent || dtc.getDataType() == dt) { dt.removeParent(this); components.remove(i); shiftOffsets(i, dtc.getLength() - 1, 0); + didChange = true; } } - adjustInternalAlignment(); + if (didChange) { + adjustInternalAlignment(); + notifySizeChanged(); + } } @Override - public void dataTypeReplaced(DataType oldDt, DataType newDt) { + public void dataTypeReplaced(DataType oldDt, DataType replacementDt) { + DataType newDt = replacementDt; try { - validateDataType(newDt); - checkAncestry(newDt); + validateDataType(replacementDt); + if (replacementDt.getDataTypeManager() != getDataTypeManager()) { + replacementDt = replacementDt.clone(dataMgr); + } + checkAncestry(replacementDt); } catch (Exception e) { - newDt = DataType.DEFAULT; + // TODO: should we use Undefined instead to avoid cases where + // DEFAULT datatype can not be used (flex array, bitfield, aligned structure) + // TODO: failing silently is rather hidden + replacementDt = DataType.DEFAULT; } boolean changed = false; - int nextIndex = 0; // index of next defined component. - Iterator it = components.iterator(); - while (it.hasNext()) { - nextIndex++; - DataTypeComponentImpl comp = it.next(); - DataType compDt = comp.getDataType(); - if (oldDt == compDt) { - oldDt.removeParent(this); - comp.setDataType(newDt); - newDt.addParent(this); - int len = newDt.getLength(); - int oldLen = comp.getLength(); - if (len > 0) { - if (len < oldLen) { - comp.setLength(len); - shiftOffsets(nextIndex, oldLen - len, 0); - } - else if (len > oldLen) { - int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); - int bytesNeeded = len - oldLen; - if (bytesNeeded <= bytesAvailable) { - comp.setLength(len); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure - doGrowStructure(bytesNeeded - bytesAvailable); - comp.setLength(len); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else { - comp.setLength(oldLen + bytesAvailable); - shiftOffsets(nextIndex, -bytesAvailable, 0); - } - } + if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == oldDt) { + flexibleArrayComponent.getDataType().removeParent(this); + if (isInvalidFlexArrayDataType(replacementDt)) { + flexibleArrayComponent = null; + Msg.error(this, "Invalid flex array replacement type " + newDt.getName() + + ", removing flex array: " + getPathName()); + } + else { + flexibleArrayComponent.setDataType(replacementDt); + replacementDt.addParent(this); + } + changed = true; + } + + for (int i = components.size() - 1; i >= 0; i--) { + + DataTypeComponentImpl comp = components.get(i); + int nextIndex = i + 1; + + boolean remove = false; + if (comp.isBitFieldComponent()) { + try { + changed |= updateBitFieldDataType(comp, oldDt, replacementDt); } + catch (InvalidDataTypeException e) { + Msg.error(this, + "Invalid bitfield replacement type " + newDt.getName() + + ", removing bitfield " + comp.getDataType().getName() + ": " + + getPathName()); + remove = true; + } + } + else if (comp.getDataType() == oldDt) { + if (replacementDt == DEFAULT && isInternallyAligned()) { + Msg.error(this, + "Invalid replacement type " + newDt.getName() + ", removing component " + + comp.getDataType().getName() + ": " + getPathName()); + remove = true; + } + else { + setComponentDataType(comp, replacementDt, nextIndex); + changed = true; + } + } + if (remove) { + // error case - remove component + oldDt.removeParent(this); + components.remove(i); + shiftOffsets(i, comp.getLength() - 1, 0); // ordinals only changed = true; } } - adjustInternalAlignment(); if (changed) { + adjustInternalAlignment(); notifySizeChanged(); } } + private void setComponentDataType(DataTypeComponentImpl comp, DataType newDt, int nextIndex) { + comp.getDataType().removeParent(this); + comp.setDataType(newDt); + newDt.addParent(this); + int len = newDt.getLength(); + int oldLen = comp.getLength(); + if (len > 0) { + if (len < oldLen) { + comp.setLength(len); + shiftOffsets(nextIndex, oldLen - len, 0); + } + else if (len > oldLen) { + int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); + int bytesNeeded = len - oldLen; + if (bytesNeeded <= bytesAvailable) { + comp.setLength(len); + shiftOffsets(nextIndex, -bytesNeeded, 0); + } + else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure + doGrowStructure(bytesNeeded - bytesAvailable); + comp.setLength(len); + shiftOffsets(nextIndex, -bytesNeeded, 0); + } + else { + comp.setLength(oldLen + bytesAvailable); + shiftOffsets(nextIndex, -bytesAvailable, 0); + } + } + } + } + @Override public DataTypeComponent[] getDefinedComponents() { return components.toArray(new DataTypeComponent[components.size()]); @@ -763,18 +1102,35 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (index < 0 || index >= numComponents) { throw new ArrayIndexOutOfBoundsException(index); } + if (dataType instanceof BitFieldDataType) { + throw new IllegalArgumentException("Components may not be replaced with a bit-field"); + } + validateDataType(dataType); - checkAncestry(dataType); - dataType = dataType.clone(getDataTypeManager()); DataTypeComponentImpl origDtc = (DataTypeComponentImpl) getComponent(index); - DataTypeComponent replacement = replace(origDtc, dataType, length, componentName, comment); + if (origDtc.isBitFieldComponent()) { + throw new IllegalArgumentException("Bit-field component may not be directly replaced"); + } + + if (dataType == DataType.DEFAULT) { + clearComponent(index); + return getComponent(index); + } + + dataType = dataType.clone(getDataTypeManager()); + checkAncestry(dataType); + + length = getPreferredComponentLength(dataType, length); + + DataTypeComponent replacement = + replaceComponent(origDtc, dataType, length, componentName, comment); adjustInternalAlignment(); return replacement; } @Override - public DataTypeComponent replace(int index, DataType dataType, int length) { + public final DataTypeComponent replace(int index, DataType dataType, int length) { return replace(index, dataType, length, null, null); } @@ -788,11 +1144,31 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur throw new IllegalArgumentException( "Offset " + offset + " is beyond end of structure (" + structLength + ")."); } + if (dataType instanceof BitFieldDataType) { + throw new IllegalArgumentException("Components may not be replaced with a bit-field"); + } + validateDataType(dataType); - checkAncestry(dataType); - dataType = dataType.clone(getDataTypeManager()); + DataTypeComponentImpl origDtc = (DataTypeComponentImpl) getComponentAt(offset); - DataTypeComponent replacement = replace(origDtc, dataType, length, componentName, comment); + if (origDtc.isBitFieldComponent()) { + throw new IllegalArgumentException("Bit-field component may not be directly replaced"); + } + + if (dataType == DataType.DEFAULT) { + int ordinal = origDtc.getOrdinal(); + clearComponent(ordinal); + return getComponent(ordinal); + } + + dataType = dataType.clone(getDataTypeManager()); + checkAncestry(dataType); + + length = getPreferredComponentLength(dataType, length); + + DataTypeComponent replacement = + replaceComponent(origDtc, dataType, length, componentName, comment); + adjustInternalAlignment(); return replacement; } @@ -804,33 +1180,31 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * @param dataType the data type of the new component * @param length the length of the new component * @param componentName the field name of the new component - * @param comment the commewnt for the new component + * @param comment the comment for the new component * @return the new component or null if the new component couldn't fit. - * @throws IllegalArgumentException if the dataType.getLength() is positive - * and does not match the given length parameter. * @throws IllegalArgumentException if the specified data type is not * allowed to replace a component in this composite data type. * For example, suppose dt1 contains dt2. Therefore it is not valid * to replace a dt2 component with dt1 since this would cause a cyclic - * dependency. + * dependency. In addition, any attempt to replace an existing bit-field + * component or specify a {@link BitFieldDatatype} will produce this error. */ - private DataTypeComponent replace(DataTypeComponentImpl origDtc, DataType dataType, int length, - String componentName, String comment) { + private DataTypeComponent replaceComponent(DataTypeComponentImpl origDtc, DataType dataType, + int length, String componentName, String comment) { + +// FIXME: Unsure how o support replace operation with bit-fields. Within unaligned structure +// the packing behavior for bit-fields prevents a one-for-one replacement and things may shift +// around which the unaligned structure tries to avoid. Insert and delete are less of a concern +// since movement already can occur, although insert at offset may not retain the offset if it +// interacts with bit-fields. int ordinal = origDtc.getOrdinal(); int newOffset = origDtc.getOffset(); int dtcLength = origDtc.getLength(); - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } - - origDtc.getDataType().removeParent(this); DataTypeComponentImpl newDtc = new DataTypeComponentImpl(dataType, this, length, ordinal, newOffset, componentName, comment); - dataType.addParent(this); + int bytesNeeded = length - dtcLength; int deltaOrdinal = -bytesNeeded; if (bytesNeeded > 0) { @@ -853,8 +1227,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } else { components.remove(index); + origDtc.getDataType().removeParent(this); } components.add(index, newDtc); + dataType.addParent(this); if (deltaOrdinal != 0) { shiftOffsets(index + 1, deltaOrdinal, 0); } @@ -935,20 +1311,21 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur /** * Adjust the alignment, packing and padding of components within this structure based upon the * current alignment and packing attributes for this structure. This method should be - * called to basically fix up the layout of the internal components of the structure + * called to fix up the layout of the internal components of the structure * after other code has changed the attributes of the structure. *
When switching between internally aligned and unaligned this method corrects the * component ordinal numbering also. * @return true if the structure was changed by this method. */ protected boolean adjustComponents() { - boolean internallyAligned = isInternallyAligned(); - boolean keepDefinedDefaults = !internallyAligned; int oldLength = structLength; + boolean changed = false; + alignment = -1; + if (!isInternallyAligned()) { - boolean changed = adjustUnalignedComponents(); + changed |= adjustUnalignedComponents(); if (changed) { if (oldLength != structLength) { notifySizeChanged(); @@ -957,63 +1334,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return changed; } - boolean compositeDBChanged = false; - boolean componentsDBChanged = false; - int packingAlignment = getPackingValue(); - - // Adjust each of the components. - int currentOrdinal = 0; - int currentOffset = 0; - int allComponentsLCM = 1; - for (DataTypeComponentImpl dataTypeComponent : components) { - - DataType componentDt = dataTypeComponent.getDataType(); - if (!keepDefinedDefaults && DataType.DEFAULT == componentDt) { - continue; // Discard a defined Default data type. - } - int componentLength = dataTypeComponent.getLength(); - int componentOrdinal = dataTypeComponent.getOrdinal(); - int componentOffset = dataTypeComponent.getOffset(); - int dtLength = componentDt.getLength(); - if (dtLength <= 0) { - dtLength = componentLength; - } - - int componentAlignment = getPackedAlignment(componentDt, dtLength, packingAlignment); - - allComponentsLCM = - DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, componentAlignment); - - int newOffset = DataOrganizationImpl.getOffset(componentAlignment, currentOffset); - currentOffset = newOffset + dtLength; - if (componentOrdinal == currentOrdinal && componentOffset == newOffset && - componentLength == dtLength) { - currentOrdinal++; - continue; // No change needed. - } - dataTypeComponent.setOffset(newOffset); - dataTypeComponent.setOrdinal(currentOrdinal); - dataTypeComponent.setLength(dtLength); - currentOrdinal++; - componentsDBChanged = true; - } - - if (flexibleArrayComponent != null) { - // account for flexible array type in any end of structure padding - DataType dataType = flexibleArrayComponent.getDataType(); - int componentAlignment = - getPackedAlignment(dataType, dataType.getLength(), packingAlignment); - currentOffset = DataOrganizationImpl.getOffset(componentAlignment, currentOffset); - allComponentsLCM = - DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, componentAlignment); - } + StructurePackResult packResult = AlignedStructurePacker.packComponents(this, components); + changed = packResult.componentsChanged; // Adjust the structure - compositeDBChanged = updateComposite(currentOrdinal, currentOffset); + changed |= updateComposite(packResult.numComponents, packResult.structureLength, + packResult.alignment); - boolean addedPadding = alignEndOfStruct(allComponentsLCM); - - if (componentsDBChanged || compositeDBChanged || addedPadding) { + if (changed) { if (oldLength != structLength) { notifySizeChanged(); } @@ -1022,63 +1350,34 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return false; } - private int getPackedAlignment(DataType componentDt, int dtLength, int packingAlignment) { - DataOrganization dataOrganization = getDataOrganization(); - int componentAlignment = dataOrganization.getAlignment(componentDt, dtLength); - int componentForcedAlignment = dataOrganization.getForcedAlignment(componentDt); - boolean componentForcingAlignment = componentForcedAlignment > 0; - if (componentForcingAlignment) { - componentAlignment = DataOrganizationImpl.getLeastCommonMultiple(componentAlignment, - componentForcedAlignment); - } - if (packingAlignment > 0) { - if (componentForcedAlignment > packingAlignment) { - componentAlignment = componentForcedAlignment; - } - else if (componentAlignment > packingAlignment) { - componentAlignment = packingAlignment; - } - } - return componentAlignment; - } - private boolean adjustUnalignedComponents() { boolean changed = false; - int currentOrdinal = 0; int componentCount = 0; int currentOffset = 0; for (DataTypeComponentImpl dataTypeComponent : components) { int componentLength = dataTypeComponent.getLength(); int componentOffset = dataTypeComponent.getOffset(); int numUndefinedsBefore = componentOffset - currentOffset; - componentCount += numUndefinedsBefore; - currentOffset += numUndefinedsBefore; - currentOrdinal += numUndefinedsBefore; - componentCount++; - currentOffset += componentLength; - if (dataTypeComponent.getOrdinal() != currentOrdinal) { - dataTypeComponent.setOrdinal(currentOrdinal); + if (numUndefinedsBefore > 0) { + componentCount += numUndefinedsBefore; + } + currentOffset = componentOffset + componentLength; + if (dataTypeComponent.getOrdinal() != componentCount) { + dataTypeComponent.setOrdinal(componentCount); changed = true; } - currentOrdinal++; + componentCount++; } int numUndefinedsAfter = structLength - currentOffset; componentCount += numUndefinedsAfter; - if (updateNumComponents(componentCount)) { + if (updateComposite(componentCount, structLength, 1)) { changed = true; } return changed; } - private boolean updateNumComponents(int currentNumComponents) { - if (numComponents != currentNumComponents) { - numComponents = currentNumComponents; - return true; - } - return false; - } - - private boolean updateComposite(int currentNumComponents, int currentLength) { + private boolean updateComposite(int currentNumComponents, int currentLength, + int currentAlignment) { boolean compositeChanged = false; if (numComponents != currentNumComponents) { numComponents = currentNumComponents; @@ -1088,28 +1387,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur structLength = currentLength; compositeChanged = true; } + if (alignment != currentAlignment) { + alignment = currentAlignment; + compositeChanged = true; + } return compositeChanged; } - private boolean alignEndOfStruct(int componentLCM) { - int minimumAlignment = getMinimumAlignment(); - int structureLength = getLength(); - if (structureLength == 0) { - return true; - } - int overallAlignment = componentLCM; - if (minimumAlignment > overallAlignment) { - // TODO Should this actually get the LeastCommonMultiple of minimumAlignment and overallAlignment? - overallAlignment = minimumAlignment; - } - int padSize = DataOrganizationImpl.getPaddingSize(overallAlignment, structLength); - if (padSize > 0) { - doGrowStructure(padSize); - return true; - } - return false; - } - private void doGrowStructure(int amount) { if (!isInternallyAligned()) { numComponents += amount; @@ -1132,10 +1416,20 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return flexibleArrayComponent; } + private boolean isInvalidFlexArrayDataType(DataType dataType) { + return (dataType == null || dataType == DataType.DEFAULT || + dataType instanceof BitFieldDataType || dataType instanceof Dynamic || + dataType instanceof FactoryDataType); + } + @Override public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, String comment) { - return doAdd(flexType, 0, name, comment, true, true); + if (isInvalidFlexArrayDataType(flexType)) { + throw new IllegalArgumentException( + "Unsupported flexType: " + flexType.getDisplayName()); + } + return doAdd(flexType, 0, true, name, comment); } @Override @@ -1166,43 +1460,3 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } } } - -class OffsetComparator implements Comparator { - - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - @Override - public int compare(Object o1, Object o2) { - if (o1 instanceof Integer) { - return -compare(o2, o1); - } - DataTypeComponent dtc = (DataTypeComponent) o1; - int offset = ((Integer) o2).intValue(); - if (offset < dtc.getOffset()) { - return 1; - } - else if (offset > dtc.getEndOffset()) { - return -1; - } - return 0; - } - -} - -class OrdinalComparator implements Comparator { - - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - @Override - public int compare(Object o1, Object o2) { - if (o1 instanceof Integer) { - return -compare(o2, o1); - } - DataTypeComponent dtc = (DataTypeComponent) o1; - int ordinal = ((Integer) o2).intValue(); - return dtc.getOrdinal() - ordinal; - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java index 0f976dc348..775441bf93 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,51 +16,8 @@ package ghidra.program.model.data; /** - * The union interface + * The Union marker interface */ public interface Union extends Composite { - /** - * - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType) - */ - public DataTypeComponent add(DataType dataType); - - /** - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType, int) - */ - public DataTypeComponent add(DataType dataType, int length); - - /** - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ - public DataTypeComponent add(DataType dataType, int length, String name, String comment); - - /** - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType) - */ - public DataTypeComponent insert(int ordinal, DataType dataType); - - /** - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType, int) - */ - public DataTypeComponent insert(int ordinal, DataType dataType, int length); - - /** - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ - public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name, - String comment); - - /** - * - * @see ghidra.program.model.data.Composite#delete(int) - */ - public void delete(int ordinal); - - /** - * - * @see ghidra.program.model.data.Composite#getComponents() - */ - public abstract DataTypeComponent[] getComponents(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java index bd50f279df..3d18353474 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java @@ -21,13 +21,14 @@ import java.util.Iterator; import ghidra.app.plugin.core.datamgr.archive.SourceArchive; import ghidra.docking.settings.Settings; import ghidra.program.model.mem.MemBuffer; +import ghidra.util.Msg; import ghidra.util.UniversalID; /** * Basic implementation of the union data type */ public class UnionDataType extends CompositeDataTypeImpl implements Union { - private ArrayList components; + private ArrayList components; private int unionLength; /** @@ -68,9 +69,6 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { this(CategoryPath.ROOT, name); } - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.util.settings.Settings, int) - */ @Override public String getRepresentation(MemBuffer buf, Settings settings, int length) { if (isNotYetDefined()) { @@ -84,111 +82,141 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { return components.size() == 0; } - /** - * - * @see ghidra.program.model.data.Composite#getComponent(int) - */ @Override public DataTypeComponent getComponent(int ordinal) { return components.get(ordinal); } - /** - * - * @see ghidra.program.model.data.Composite#getComponents() - */ @Override public DataTypeComponent[] getComponents() { return components.toArray(new DataTypeComponent[components.size()]); } - /** - * - * @see ghidra.program.model.data.Composite#getNumComponents() - */ @Override public int getNumComponents() { return components.size(); } - /** - * - * @see ghidra.program.model.data.Composite#add(ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent add(DataType dataType, int length, String componentName, String comment) { - int oldLength = unionLength; DataTypeComponent dtc = doAdd(dataType, length, componentName, comment); - adjustInternalAlignment(); - if (oldLength != unionLength) { - notifySizeChanged(); - } + adjustLength(true); return dtc; } + private int getBitFieldAllocation(BitFieldDataType bitfieldDt) { + + BitFieldPacking bitFieldPacking = getBitFieldPacking(); + if (bitFieldPacking.useMSConvention()) { + return bitfieldDt.getBaseTypeSize(); + } + + if (bitfieldDt.getBitSize() == 0) { + return 0; + } + + int length = bitfieldDt.getBaseTypeSize(); + int packValue = getPackingValue(); + if (packValue != NOT_PACKING && length > packValue) { + length = + DataOrganizationImpl.getLeastCommonMultiple(bitfieldDt.getStorageSize(), packValue); + } + return length; + } + DataTypeComponent doAdd(DataType dataType, int length, String componentName, String comment) { + validateDataType(dataType); + dataType = dataType.clone(getDataTypeManager()); checkAncestry(dataType); - if (length < 1) { - throw new IllegalArgumentException("Length must be >= 1!"); - } + length = getPreferredComponentLength(dataType, length); - dataType = dataType.clone(getDataTypeManager()); - - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } - - DataTypeComponent dtc = new DataTypeComponentImpl(dataType, this, length, components.size(), - 0, componentName, comment); + DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, + components.size(), 0, componentName, comment); dataType.addParent(this); components.add(dtc); - unionLength = Math.max(unionLength, length); + return dtc; } - /** - * - * @see ghidra.program.model.data.Composite#insert(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String) - */ @Override public DataTypeComponent insert(int ordinal, DataType dataType, int length, String componentName, String comment) { validateDataType(dataType); - checkAncestry(dataType); dataType = dataType.clone(getDataTypeManager()); + checkAncestry(dataType); - // TODO Is this the right place to adjust the length? - int dtLength = dataType.getLength(); - if (dtLength > 0 && dtLength < length) { - length = dtLength; - } + length = getPreferredComponentLength(dataType, length); - DataTypeComponent dtc = + DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal, 0, componentName, comment); dataType.addParent(this); shiftOrdinals(ordinal, 1); components.add(ordinal, dtc); - int oldLength = unionLength; - unionLength = Math.max(unionLength, length); - adjustInternalAlignment(); - if (oldLength != unionLength) { - notifySizeChanged(); - } + + adjustLength(true); + return dtc; + } + + @Override + public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, + DataType baseDataType, int bitSize, String componentName, String comment) + throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { + + if (ordinal < 0 || ordinal > components.size()) { + throw new ArrayIndexOutOfBoundsException(ordinal); + } + + BitFieldDataType.checkBaseDataType(baseDataType); + baseDataType = baseDataType.clone(getDataTypeManager()); + + if (isInternallyAligned()) { + BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize); + return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); + } + + if (byteWidth <= 0) { + throw new IllegalArgumentException("Invalid byteWidth"); + } + + // TODO: How should zero-length bitfields be handled ? + + // handle unaligned case - use minimal storage + // bitfield value will be forced based upon byteWidth, bitSize and endianess + boolean bigEndian = getDataOrganization().isBigEndian(); + int effectiveBitSize = + BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength()); + int storageSize = BitFieldDataType.getMinimumStorageSize(effectiveBitSize); + if (byteWidth < storageSize) { + throw new IllegalArgumentException( + "Bitfield does not fit within specified constraints"); + } + int storageBitOffset = 0; + if (bigEndian) { + storageBitOffset = (8 * storageSize) - effectiveBitSize; + } + + BitFieldDataType bitfieldDt = + new BitFieldDataType(baseDataType, bitSize, storageBitOffset, storageSize); + + DataTypeComponentImpl dtc = new DataTypeComponentImpl(bitfieldDt, this, storageSize, + ordinal, 0, componentName, comment); + + bitfieldDt.addParent(this); // currently has no affect + + shiftOrdinals(ordinal, 1); + components.add(ordinal, dtc); + + adjustLength(true); + return dtc; } - /** - * - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { if (unionLength == 0) { @@ -217,20 +245,12 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { return union; } - /** - * @see ghidra.program.model.data.Composite#delete(int) - */ @Override public void delete(int ordinal) { - int oldLength = unionLength; DataTypeComponent dtc = components.remove(ordinal); dtc.getDataType().removeParent(this); shiftOrdinals(ordinal, -1); - computeUnionLength(); - adjustInternalAlignment(); - if (oldLength != unionLength) { - notifySizeChanged(); - } + adjustLength(true); } @Override @@ -240,17 +260,24 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { } } - private void computeUnionLength() { + private void adjustLength(boolean notify) { + int oldLength = unionLength; unionLength = 0; - for (int i = 0; i < components.size(); i++) { - unionLength = Math.max(unionLength, (components.get(i)).getLength()); + for (DataTypeComponent dtc : components) { + + int length = dtc.getLength(); + if (isInternallyAligned() && dtc.isBitFieldComponent()) { + // revise length to reflect compiler bitfield allocation rules + length = getBitFieldAllocation((BitFieldDataType) dtc.getDataType()); + } + + unionLength = Math.max(length, unionLength); + } + if (notify && oldLength != unionLength) { + notifySizeChanged(); } } - /** - * - * @see ghidra.program.model.data.DataType#isEquivalent(ghidra.program.model.data.DataType) - */ @Override public boolean isEquivalent(DataType dt) { if (dt == this) { @@ -288,85 +315,109 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { private void shiftOrdinals(int ordinal, int deltaOrdinal) { for (int i = ordinal; i < components.size(); i++) { - DataTypeComponentImpl dtc = (DataTypeComponentImpl) components.get(i); + DataTypeComponentImpl dtc = components.get(i); dtc.setOrdinal(dtc.getOrdinal() + deltaOrdinal); } } @Override public void dataTypeAlignmentChanged(DataType dt) { - // TODO I don't think we need to do anything here. - adjustInternalAlignment(); + if (isInternallyAligned()) { + adjustInternalAlignment(); + } } - /** - * @see ghidra.program.model.data.DataType#dataTypeSizeChanged(ghidra.program.model.data.DataType) - */ @Override public void dataTypeSizeChanged(DataType dt) { - int oldLength = unionLength; - unionLength = 0; - for (int i = 0; i < components.size(); i++) { - DataTypeComponentImpl dtc = (DataTypeComponentImpl) components.get(i); - DataType tmpDt = dtc.getDataType(); - int tmpLen = tmpDt.getLength(); - if ((tmpDt.isEquivalent(dt)) && (tmpLen > 0) && (tmpLen != dtc.getLength())) { - dtc.setLength(tmpLen); - } - unionLength = Math.max(unionLength, dtc.getLength()); - } - adjustInternalAlignment(); - if (oldLength != unionLength) { - notifySizeChanged(); - } - } - - /* (non-Javadoc) - * @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType) - */ - @Override - public void dataTypeReplaced(DataType oldDt, DataType newDt) { - try { - validateDataType(newDt); - checkAncestry(newDt); - } - catch (Exception e) { - newDt = DataType.DEFAULT; - } - int oldLength = unionLength; - unionLength = 0; boolean changed = false; - for (int i = 0; i < components.size(); i++) { - DataTypeComponentImpl dtc = (DataTypeComponentImpl) components.get(i); - if (dtc.getDataType() == oldDt) { - oldDt.removeParent(this); - dtc.setDataType(newDt); - newDt.addParent(this); - int len = newDt.getLength(); - if (len > 0) { - dtc.setLength(len); - } + for (DataTypeComponentImpl dtc : components) { + int length = dtc.getLength(); + if (dtc.getDataType() == dt) { + length = getPreferredComponentLength(dt, length); + dtc.setLength(length); changed = true; } } if (changed) { - computeUnionLength(); - adjustInternalAlignment(); - if (oldLength != unionLength) { - notifySizeChanged(); - } + adjustLength(true); + } + } + + @Override + public void dataTypeReplaced(DataType oldDt, DataType newDt) { + DataType replacementDt = newDt; + try { + validateDataType(replacementDt); + if (replacementDt.getDataTypeManager() != dataMgr) { + replacementDt = replacementDt.clone(dataMgr); + } + checkAncestry(replacementDt); + } + catch (Exception e) { + // TODO: should we use Undefined instead since we do not support + // DEFAULT in Unions + replacementDt = DataType.DEFAULT; + } + boolean changed = false; + for (int i = components.size() - 1; i >= 0; i--) { + + DataTypeComponentImpl dtc = components.get(i); + + boolean remove = false; + if (dtc.isBitFieldComponent()) { + try { + changed |= updateBitFieldDataType(dtc, oldDt, replacementDt); + } + catch (InvalidDataTypeException e) { + Msg.error(this, + "Invalid bitfield replacement type " + newDt.getName() + + ", removing bitfield " + dtc.getDataType().getName() + ": " + + getPathName()); + remove = true; + } + } + else if (dtc.getDataType() == oldDt) { + if (replacementDt == DEFAULT) { + Msg.error(this, + "Invalid replacement type " + newDt.getName() + ", removing component " + + dtc.getDataType().getName() + ": " + getPathName()); + remove = true; + } + else { + oldDt.removeParent(this); + dtc.setDataType(replacementDt); + replacementDt.addParent(this); + int len = replacementDt.getLength(); + if (len > 0) { + dtc.setLength(len); + } + changed = true; + } + } + if (remove) { + // error case - remove component + oldDt.removeParent(this); + components.remove(i); + shiftOrdinals(i, -1); + changed = true; + } + } + if (changed) { + adjustLength(true); } } - /** - * @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType) - */ @Override public void dataTypeDeleted(DataType dt) { boolean didDelete = false; - for (int i = components.size() - 1; i >= 0; i--) { - DataTypeComponent dtc = components.get(i); - if (dtc.getDataType() == dt) { + for (int i = components.size() - 1; i >= 0; i--) { // reverse order + DataTypeComponentImpl dtc = components.get(i); + boolean removeBitFieldComponent = false; + if (dtc.isBitFieldComponent()) { + BitFieldDataType bitfieldDt = (BitFieldDataType) dtc.getDataType(); + removeBitFieldComponent = bitfieldDt.getBaseDataType() == dt; + } + if (removeBitFieldComponent || dtc.getDataType() == dt) { dt.removeParent(this); components.remove(i); shiftOrdinals(i, -1); @@ -374,12 +425,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { } } if (didDelete) { - int oldLength = unionLength; - computeUnionLength(); - adjustInternalAlignment(); - if (oldLength != unionLength) { - notifySizeChanged(); - } + adjustLength(true); } } @@ -398,48 +444,33 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { if (!(dataType instanceof Union)) { throw new IllegalArgumentException(); } - int oldLength = unionLength; - doReplaceWith((Union) dataType); - if (oldLength != unionLength) { - notifySizeChanged(); - } - } - private void doReplaceWith(Union union) { - Iterator it = components.iterator(); + Union union = (Union) dataType; + + Iterator it = components.iterator(); while (it.hasNext()) { DataTypeComponent dtc = it.next(); dtc.getDataType().removeParent(this); } components.clear(); - unionLength = 0; + setAlignment(union); DataTypeComponent[] compArray = union.getComponents(); for (int i = 0; i < compArray.length; i++) { DataTypeComponent dtc = compArray[i]; DataType dt = dtc.getDataType(); - validateDataType(dt); - dt = dt.clone(getDataTypeManager()); - int dtLength = dt.getLength(); - if (dtLength <= 0) { - dtLength = dtc.getLength(); - } - doAdd(dt, dtLength, dtc.getFieldName(), dtc.getComment()); + doAdd(dt, dtc.getLength(), dtc.getFieldName(), dtc.getComment()); } - setDataAlignmentInfo(union); + + adjustLength(true); } - /** - * @see ghidra.program.model.data.DataType#dataTypeNameChanged(ghidra.program.model.data.DataType, java.lang.String) - */ @Override public void dataTypeNameChanged(DataType dt, String oldName) { + // ignored } - /** - * @see ghidra.program.model.data.DataType#dependsOn(ghidra.program.model.data.DataType) - */ @Override public boolean dependsOn(DataType dt) { if (getNumComponents() == 1) { @@ -454,33 +485,15 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union { return "UNION_" + getName(); } - public void align(DataOrganization dataOrganization) { - // TODO Auto-generated method stub - } - - private void adjustLength() { - // TODO WHat should we do here? - } - @Override - public int getPackingValue() { - return packingValue; - } - - @Override - public void setPackingValue(int packingValue) { - this.packingValue = packingValue; - adjustInternalAlignment(); + public void realign() { + if (isInternallyAligned()) { + adjustInternalAlignment(); + } } @Override public void adjustInternalAlignment() { - adjustLength(); + adjustLength(true); } - - @Override - public void realign() { - adjustInternalAlignment(); - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java index dd4a3022f6..cc088cf221 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java @@ -43,7 +43,10 @@ public class UnsignedCharDataType extends CharDataType { } @Override - public DataType clone(DataTypeManager dtm) { + public UnsignedCharDataType clone(DataTypeManager dtm) { + if (dtm == getDataTypeManager()) { + return this; + } return new UnsignedCharDataType(dtm); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java index 4340f531e0..883f75815d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java @@ -33,20 +33,23 @@ public class UnsignedInteger16DataType extends AbstractIntegerDataType { super("uint16", false, dtm); } + @Override public String getDescription() { return "Unsigned 16-Byte Integer"; } + @Override public int getLength() { return 16; } @Override - public DataType getOppositeSignednessDataType() { - return Integer16DataType.dataType; + public Integer16DataType getOppositeSignednessDataType() { + return Integer16DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedInteger16DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java index d15b5da08c..e9ac687917 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,20 +37,23 @@ public class UnsignedInteger3DataType extends AbstractIntegerDataType { super("uint3", false, dtm); } + @Override public String getDescription() { return "Unsigned 3-Byte Integer)"; } + @Override public int getLength() { return 3; } @Override - public DataType getOppositeSignednessDataType() { - return Integer3DataType.dataType; + public Integer3DataType getOppositeSignednessDataType() { + return Integer3DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedInteger3DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java index f304456ef5..6acdeb20e9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class UnsignedInteger5DataType extends AbstractIntegerDataType { super("uint5", false, dtm); } + @Override public String getDescription() { return "Unsigned 5-Byte Integer"; } + @Override public int getLength() { return 5; } @Override - public DataType getOppositeSignednessDataType() { - return Integer5DataType.dataType; + public Integer5DataType getOppositeSignednessDataType() { + return Integer5DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedInteger5DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java index 26b2e623c8..e98562d40f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class UnsignedInteger6DataType extends AbstractIntegerDataType { super("uint6", false, dtm); } + @Override public String getDescription() { return "Unsigned 6-Byte Integer"; } + @Override public int getLength() { return 6; } @Override - public DataType getOppositeSignednessDataType() { - return Integer6DataType.dataType; + public Integer6DataType getOppositeSignednessDataType() { + return Integer6DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedInteger6DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java index 028e566082..381c790f14 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,20 +30,23 @@ public class UnsignedInteger7DataType extends AbstractIntegerDataType { super("uint7", false, dtm); } + @Override public String getDescription() { return "Unsigned 7-Byte Integer"; } + @Override public int getLength() { return 7; } @Override - public DataType getOppositeSignednessDataType() { - return Integer7DataType.dataType; + public Integer7DataType getOppositeSignednessDataType() { + return Integer7DataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedInteger7DataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java index 18725e47f2..c5b4985426 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +37,7 @@ public class UnsignedIntegerDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getIntegerSize(); } @@ -54,6 +54,7 @@ public class UnsignedIntegerDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Unsigned Integer (compiler-specific size)"; } @@ -64,11 +65,12 @@ public class UnsignedIntegerDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public IntegerDataType getOppositeSignednessDataType() { return IntegerDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedIntegerDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java index 9d8f093675..e76d444814 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +37,7 @@ public class UnsignedLongDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getLongSize(); } @@ -54,6 +54,7 @@ public class UnsignedLongDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Unsigned Long Integer (compiler-specific size)"; } @@ -64,11 +65,12 @@ public class UnsignedLongDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public LongDataType getOppositeSignednessDataType() { return LongDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedLongDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java index 79d38ec86a..df313ce3ea 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +37,7 @@ public class UnsignedLongLongDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getLongLongSize(); } @@ -54,6 +54,7 @@ public class UnsignedLongLongDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Unsigned Long Long Integer (compiler-specific size)"; } @@ -64,11 +65,12 @@ public class UnsignedLongLongDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public LongLongDataType getOppositeSignednessDataType() { return LongLongDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedLongLongDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java index b0f8b8fe8c..dd7f743345 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +37,7 @@ public class UnsignedShortDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getLength() */ + @Override public int getLength() { return getDataOrganization().getShortSize(); } @@ -54,6 +54,7 @@ public class UnsignedShortDataType extends AbstractIntegerDataType { * * @see ghidra.program.model.data.DataType#getDescription() */ + @Override public String getDescription() { return "Unsigned Short Integer (compiler-specific size)"; } @@ -64,11 +65,12 @@ public class UnsignedShortDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { + public ShortDataType getOppositeSignednessDataType() { return ShortDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public UnsignedShortDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java index b40e32d580..f33ed19a70 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +33,12 @@ public class WordDataType extends AbstractIntegerDataType { super("word", false, dtm); } + @Override public String getDescription() { return "Unsigned Word (dw, 2-bytes)"; } + @Override public int getLength() { return 2; } @@ -48,11 +49,12 @@ public class WordDataType extends AbstractIntegerDataType { } @Override - public DataType getOppositeSignednessDataType() { - return SignedWordDataType.dataType; + public SignedWordDataType getOppositeSignednessDataType() { + return SignedWordDataType.dataType.clone(getDataTypeManager()); } - public DataType clone(DataTypeManager dtm) { + @Override + public WordDataType clone(DataTypeManager dtm) { if (dtm == getDataTypeManager()) { return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java index 890fb19610..4c40319f79 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java @@ -41,7 +41,7 @@ public class BasicCompilerSpec implements CompilerSpec { public static final String DECOMPILER_OUTPUT_LANGUAGE = "Output Language"; public final static DecompilerLanguage DECOMPILER_OUTPUT_DEF = DecompilerLanguage.C_LANGUAGE; public final static String DECOMPILER_OUTPUT_DESC = - "Select the source language output by the decompiler."; + "Select the source language output by the decompiler."; private static final String EVALUATION_MODEL_PROPERTY_NAME = "Prototype Evaluation"; public static final String STACK_SPACE_NAME = "stack"; @@ -51,7 +51,7 @@ public class BasicCompilerSpec implements CompilerSpec { private String sourceName; private final SleighLanguage language; private DataOrganizationImpl dataOrganization; - private List ctxsetting = new ArrayList(); + private List ctxsetting = new ArrayList<>(); private PrototypeModel defaultModel; private PrototypeModel defaultEvaluationModel; private PrototypeModel[] models; @@ -62,12 +62,11 @@ public class BasicCompilerSpec implements CompilerSpec { private AddressSpace joinSpace; private boolean stackGrowsNegative = true; private boolean reverseJustifyStack = false; - private Map spaceBases = new HashMap(); + private Map spaceBases = new HashMap<>(); private PcodeInjectLibrary pcodeInject; private AddressSet globalSet; - private LinkedHashMap properties = new LinkedHashMap(); - private Map callingConventionMap = - new HashMap(); + private LinkedHashMap properties = new LinkedHashMap<>(); + private Map callingConventionMap = new HashMap<>(); private String[] evaluationModelChoices; private String specString; private ResourceFile specFile; @@ -119,7 +118,7 @@ public class BasicCompilerSpec implements CompilerSpec { Throwable cause = e.getCause(); // Recover the cause (from the validator exception) if (cause != null) { if (cause instanceof SAXException || cause instanceof IOException) - parseException = (Exception)cause; + parseException = (Exception) cause; } } catch (FileNotFoundException e) { @@ -137,7 +136,7 @@ public class BasicCompilerSpec implements CompilerSpec { if (parseException != null) { throw new CompilerSpecNotFoundException(language.getLanguageID(), - description.getCompilerSpecID(), cspecFile.getName(),parseException); + description.getCompilerSpecID(), cspecFile.getName(), parseException); } } @@ -155,7 +154,7 @@ public class BasicCompilerSpec implements CompilerSpec { @SuppressWarnings("unchecked") private void buildInjectLibrary() { String classname = - language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); + language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); if (classname == null) { pcodeInject = new PcodeInjectLibrary(language); // This is the default implementation return; @@ -163,23 +162,24 @@ public class BasicCompilerSpec implements CompilerSpec { try { Class c = Class.forName(classname); if (!PcodeInjectLibrary.class.isAssignableFrom(c)) { - Msg.error(this, "Language " + language.getLanguageID() + - " does not specify a valid " + + Msg.error(this, + "Language " + language.getLanguageID() + " does not specify a valid " + GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); throw new RuntimeException(classname + " does not implement interface " + - PcodeInjectLibrary.class.getName()); + PcodeInjectLibrary.class.getName()); } Class injectLibraryClass = - (Class) c; + (Class) c; Constructor constructor = - injectLibraryClass.getConstructor(SleighLanguage.class); + injectLibraryClass.getConstructor(SleighLanguage.class); pcodeInject = constructor.newInstance(language); } catch (Exception e) { Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " + - GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); - throw new RuntimeException("Failed to instantiate " + classname + " for language " + - language.getLanguageID(), e); + GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS); + throw new RuntimeException( + "Failed to instantiate " + classname + " for language " + language.getLanguageID(), + e); } List additionalInject = language.getAdditionalInject(); if (additionalInject != null) { @@ -188,7 +188,7 @@ public class BasicCompilerSpec implements CompilerSpec { } } } - + private void addThisCallConventionIfMissing() { boolean foundThisCall = false; for (PrototypeModel model : models) { @@ -434,7 +434,7 @@ public class BasicCompilerSpec implements CompilerSpec { private void restoreXml(XmlPullParser parser) throws XmlParseException { stackPointer = null; - List modelList = new ArrayList(); + List modelList = new ArrayList<>(); String evalCurrentPrototype = null; parser.start("compiler_spec"); @@ -489,9 +489,9 @@ public class BasicCompilerSpec implements CompilerSpec { } if (stackPointer == null) { - stackSpace = - new GenericAddressSpace(STACK_SPACE_NAME, language.getDefaultSpace().getSize(), - language.getDefaultSpace().getAddressableUnitSize(), AddressSpace.TYPE_STACK, 0); + stackSpace = new GenericAddressSpace(STACK_SPACE_NAME, + language.getDefaultSpace().getSize(), + language.getDefaultSpace().getAddressableUnitSize(), AddressSpace.TYPE_STACK, 0); } buildModelArrays(modelList); @@ -534,22 +534,22 @@ public class BasicCompilerSpec implements CompilerSpec { parser.end(); } - private void restoreDataOrganization(XmlPullParser parser) { + private void restoreDataOrganization(XmlPullParser parser) throws XmlParseException { parser.start(); while (parser.peek().isStart()) { XmlElement subel = parser.start(); String name = subel.getName(); - + if (name.equals("char_type")) { String boolStr = subel.getAttribute("signed"); dataOrganization.setCharIsSigned(SpecXmlUtils.decodeBoolean(boolStr)); parser.end(subel); continue; } - + String value = subel.getAttribute("value"); - + if (name.equals("absolute_max_alignment")) { dataOrganization.setAbsoluteMaxAlignment(SpecXmlUtils.decodeInt(value)); } @@ -605,12 +605,37 @@ public class BasicCompilerSpec implements CompilerSpec { parser.end(subsubel); } } + else if (name.equals("bitfield_packing")) { + dataOrganization.setBitFieldPacking(parseBitFieldPacking(parser)); + } parser.end(subel); } parser.end(); } + private BitFieldPacking parseBitFieldPacking(XmlPullParser parser) { + BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); + while (parser.peek().isStart()) { + XmlElement subel = parser.start(); + String name = subel.getName(); + String value = subel.getAttribute("value"); + + if (name.equals("use_MS_convention")) { + bitFieldPacking.setUseMSConvention(SpecXmlUtils.decodeBoolean(value)); + } + else if (name.equals("type_alignment_enabled")) { + bitFieldPacking.setTypeAlignmentEnabled(SpecXmlUtils.decodeBoolean(value)); + } + else if (name.equals("zero_length_boundary")) { + bitFieldPacking.setZeroLengthBoundary(SpecXmlUtils.decodeInt(value)); + } + + parser.end(subel); + } + return bitFieldPacking; + } + private void restoreSpaceBase(XmlPullParser parser) { XmlElement el = parser.start(); String name = el.getAttribute("name"); @@ -749,8 +774,8 @@ public class BasicCompilerSpec implements CompilerSpec { stackGrowsNegative = false; } else { - throw new SleighException("Bad stack growth " + growth + - " should be 'positive' or 'negative'"); + throw new SleighException( + "Bad stack growth " + growth + " should be 'positive' or 'negative'"); } parser.end(el); } @@ -833,7 +858,7 @@ public class BasicCompilerSpec implements CompilerSpec { public DecompilerLanguage getDecompilerOutputLanguage(Program program) { Options options = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME); if (options.contains(DECOMPILER_OUTPUT_LANGUAGE)) { - return options.getEnum(DECOMPILER_OUTPUT_LANGUAGE,DECOMPILER_OUTPUT_DEF); + return options.getEnum(DECOMPILER_OUTPUT_LANGUAGE, DECOMPILER_OUTPUT_DEF); } return DECOMPILER_OUTPUT_DEF; } @@ -846,21 +871,15 @@ public class BasicCompilerSpec implements CompilerSpec { // for upgrading/moving old property values. Options decompilerPropertyList = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME); - decompilerPropertyList.registerOption( - EVALUATION_MODEL_PROPERTY_NAME, - OptionType.STRING_TYPE, - evaluationModelChoices[0], - null, + decompilerPropertyList.registerOption(EVALUATION_MODEL_PROPERTY_NAME, + OptionType.STRING_TYPE, evaluationModelChoices[0], null, "Select the default function prototype/evaluation model to be used during Decompiler analysis", new StringWithChoicesEditor(evaluationModelChoices)); - + if (decompilerPropertyList.contains(DECOMPILER_OUTPUT_LANGUAGE)) { - decompilerPropertyList.registerOption( - DECOMPILER_OUTPUT_LANGUAGE, - DECOMPILER_OUTPUT_DEF, - null, - DECOMPILER_OUTPUT_DESC); - + decompilerPropertyList.registerOption(DECOMPILER_OUTPUT_LANGUAGE, DECOMPILER_OUTPUT_DEF, + null, DECOMPILER_OUTPUT_DESC); + } Options analysisPropertyList = @@ -939,12 +958,12 @@ public class BasicCompilerSpec implements CompilerSpec { * @param program to be enabled */ public static void enableJavaLanguageDecompilation(Program program) { - Options decompilerPropertyList = program.getOptions(BasicCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME); - decompilerPropertyList.registerOption( - BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE, - BasicCompilerSpec.DECOMPILER_OUTPUT_DEF, - null, + Options decompilerPropertyList = + program.getOptions(BasicCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME); + decompilerPropertyList.registerOption(BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE, + BasicCompilerSpec.DECOMPILER_OUTPUT_DEF, null, BasicCompilerSpec.DECOMPILER_OUTPUT_DESC); - decompilerPropertyList.setEnum(BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE, DecompilerLanguage.JAVA_LANGUAGE); + decompilerPropertyList.setEnum(BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE, + DecompilerLanguage.JAVA_LANGUAGE); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java index 3777ed2209..a253649988 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java @@ -15,6 +15,8 @@ */ package ghidra.program.model.listing; +import java.util.List; + import ghidra.docking.settings.Settings; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; @@ -23,7 +25,7 @@ import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.Reference; /** - * Interface for interacting with data at an address in a program. + * Interface for interacting with data at an address in a program. */ public interface Data extends CodeUnit, Settings { @@ -35,7 +37,7 @@ public interface Data extends CodeUnit, Settings { /** * Get the class used to express the value of this data. - * NOTE: This determination is made based upon data type + * NOTE: This determination is made based upon data type * and settings only and does not examine memory bytes * which are used to construct the data value object. * @return value class or null if a consistent class is not @@ -74,7 +76,7 @@ public interface Data extends CodeUnit, Settings { public DataType getDataType(); /** - * If the dataType is a typeDef, then the typeDef's base type is returned, + * If the dataType is a typeDef, then the typeDef's base type is returned, * otherwise, the datatType is returned. */ public DataType getBaseDataType(); @@ -200,9 +202,22 @@ public interface Data extends CodeUnit, Settings { */ Data getComponentAt(int offset); + /** + * Returns a list of all the immediate child components that contain the byte at the + * given offset. + *

+ * For a union, this will return all the components (if the offset is 0). For a structure, + * this will be either a single non bit field element or a list of bit field elements. + * @param offset the amount to add to this data items address to get the + * address of the requested data item. + * @return a list of all the immediate child components that contain the byte at the + * given offset. + */ + List getComponentsContaining(int offset); + /** * Returns the primitive component that is at this offset. This is useful - * for data items are made up of multiple layers of other data items. This + * for data items are made up of multiple layers of other data items. This * method immediately goes to the lowest level data item. */ Data getPrimitiveAt(int offset); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java index 37b21da088..44fbe772be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java @@ -17,6 +17,7 @@ package ghidra.program.model.listing; import java.math.BigInteger; import java.util.Iterator; +import java.util.List; import ghidra.docking.settings.Settings; import ghidra.program.model.address.Address; @@ -33,7 +34,7 @@ import ghidra.util.prop.PropertyVisitor; /** * DataStub can be extended for use by tests. It throws an UnsupportedOperationException - * for all methods in the Data interface. Any method that is needed for your test can then + * for all methods in the Data interface. Any method that is needed for your test can then * be overridden so it can provide its own test implementation and return value. */ public class DataStub implements Data { @@ -526,6 +527,11 @@ public class DataStub implements Data { throw new UnsupportedOperationException(); } + @Override + public List getComponentsContaining(int offset) { + throw new UnsupportedOperationException(); + } + @Override public Data getPrimitiveAt(int offset) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java index d2e23c54f9..f6a9e0295e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java @@ -263,6 +263,9 @@ public class VariableUtilities { */ public static DataType checkDataType(DataType dataType, boolean voidOK, int defaultSize, Program program) throws InvalidInputException { + if (dataType instanceof BitFieldDataType) { + throw new InvalidInputException("Bitfields not supported for variable"); + } if (dataType == null) { if (voidOK) { return VoidDataType.dataType; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java index 71d95b1e02..882f632ae9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java @@ -35,8 +35,9 @@ public class ByteMemBufferImpl implements MemBuffer { /** * Construct a ByteMemBufferImpl object - * @param addr that address to associate with the bytes - * @param bytes the data that normally would be comming from memory. + * @param addr the address to associate with the bytes + * @param bytes the data that normally would be coming from memory. + * @param isBigEndian true for BigEndian, false for LittleEndian. */ public ByteMemBufferImpl(Address addr, byte[] bytes, boolean isBigEndian) { this.addr = addr; @@ -44,6 +45,26 @@ public class ByteMemBufferImpl implements MemBuffer { this.isBigEndian = isBigEndian; } + /** + * Convenience constructor using varargs for specifying byte values. + * @param addr the address to associate with the bytes + * @param isBigEndian true for BigEndian, false for LittleEndian. + * @param byteValues varargs for specifying the individual byte values. The int argument + * will be truncated to a byte value. + */ + public ByteMemBufferImpl(Address addr, boolean isBigEndian, int... byteValues) { + this.addr = addr; + this.isBigEndian = isBigEndian; + bytes = new byte[byteValues.length]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) byteValues[i]; + } + } + + /** + * Get number of bytes contained within buffer + * @return byte count + */ public int getLength() { return bytes.length; } diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeUtilitiesTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeUtilitiesTest.java index a68bf097d8..1bc7aa8c3d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeUtilitiesTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeUtilitiesTest.java @@ -22,14 +22,10 @@ import java.util.Collection; import org.junit.Test; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.program.database.data.DataTypeUtilities; -public class DataTypeUtilitiesTest extends AbstractGenericTest { - - public DataTypeUtilitiesTest() { - super(); - } +public class DataTypeUtilitiesTest extends AbstractGTest { @Test public void testGetContainedDataTypes() { diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java index 35f8693d7e..770428bbed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java @@ -22,11 +22,11 @@ import java.io.StringWriter; import org.junit.*; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitorAdapter; -public class DataTypeWriterTest extends AbstractGenericTest { +public class DataTypeWriterTest extends AbstractGTest { private static String EOL = System.getProperty("line.separator"); diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java index 9b8fc57680..863daedf9e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java @@ -21,53 +21,36 @@ import java.util.NoSuchElementException; import org.junit.*; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.util.task.TaskMonitorAdapter; /** * Tests for Enum data types. */ -public class EnumTest extends AbstractGenericTest { +public class EnumTest extends AbstractGTest { private DataTypeManager dataMgr; - private int transactionID; - /** - * Constructor for EnumTest. - * @param arg0 - */ - public EnumTest() { - super(); + @Before + public void setUp() throws Exception { + dataMgr = new StandAloneDataTypeManager("Test"); + dataMgr.startTransaction(""); } - /* - * @see TestCase#setUp() - */ - @Before - public void setUp() throws Exception { - dataMgr = new StandAloneDataTypeManager("Test"); - transactionID = dataMgr.startTransaction(""); - } - @After - public void tearDown() { - dataMgr.endTransaction(transactionID, true); - dataMgr.close(); - } - -@Test - public void testCreateEnum() throws Exception { + @Test + public void testCreateEnum() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 0); enumm.add("Green", 1); enumm.add("Blue", 2); - + Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - + + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + assertNotNull(enummDT); - + assertEquals("Color", enummDT.getName()); assertEquals(0, enummDT.getValue("Red")); assertEquals(1, enummDT.getValue("Green")); @@ -75,56 +58,59 @@ public class EnumTest extends AbstractGenericTest { assertEquals(1, enummDT.getLength()); assertEquals(3, enummDT.getCount()); - - assertTrue(enumm.isEquivalent(enummDT)); + + assertTrue(enumm.isEquivalent(enummDT)); assertTrue(enummDT.isEquivalent(enumm)); - + assertEquals(c.getCategoryPath(), enummDT.getCategoryPath()); - + assertNotNull(c.getDataType("Color")); } -@Test - public void testRemoveValue() throws Exception { + + @Test + public void testRemoveValue() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 0); enumm.add("Green", 1); enumm.add("Blue", 2); - + Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); assertEquals(3, enummDT.getCount()); enummDT.remove("Green"); assertEquals(2, enummDT.getCount()); - + } -@Test - public void testAddValue() throws Exception { + + @Test + public void testAddValue() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 0); enumm.add("Green", 1); enumm.add("Blue", 2); - + Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - + + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + enummDT.add("Purple", 7); assertEquals(4, enummDT.getCount()); assertEquals(7, enummDT.getValue("Purple")); } -@Test - public void testEditValue() throws Exception { + + @Test + public void testEditValue() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); enummDT.remove("Blue"); assertEquals(2, enummDT.getCount()); enummDT.add("Blue", 30); @@ -132,100 +118,101 @@ public class EnumTest extends AbstractGenericTest { assertEquals("Blue", enummDT.getName(30)); assertNull(enummDT.getName(20)); } - -@Test - public void testCloneRetain() throws Exception { + + @Test + public void testCloneRetain() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - Enum copyDT = (Enum)enummDT.clone(null); + Enum copyDT = (Enum) enummDT.clone(null); assertNotNull(copyDT); - - Enum c2 = (Enum)root.addDataType(copyDT, DataTypeConflictHandler.DEFAULT_HANDLER); + + Enum c2 = (Enum) root.addDataType(copyDT, DataTypeConflictHandler.DEFAULT_HANDLER); assertNotNull(c2); assertTrue(copyDT.isEquivalent(c2)); } - -@Test - public void testCopyDontRetain() throws Exception { + + @Test + public void testCopyDontRetain() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - Enum copyDT = (Enum)enummDT.copy(null); + Enum copyDT = (Enum) enummDT.copy(null); assertNotNull(copyDT); - - Enum c2 = (Enum)root.addDataType(copyDT, DataTypeConflictHandler.DEFAULT_HANDLER); + + Enum c2 = (Enum) root.addDataType(copyDT, DataTypeConflictHandler.DEFAULT_HANDLER); assertNotNull(c2); assertTrue(copyDT.isEquivalent(c2)); } - -@Test - public void testRemoveEnum() throws Exception { + + @Test + public void testRemoveEnum() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); assertNotNull(enummDT); - + c.remove(enummDT, TaskMonitorAdapter.DUMMY_MONITOR); assertNull(c.getDataType("Color")); - + assertTrue(enummDT.isDeleted()); - + } - -@Test - public void testMoveEnum() throws Exception { + + @Test + public void testMoveEnum() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + root.moveDataType(enummDT, null); assertNotNull(root.getDataType(enumm.getName())); assertNull(c.getDataType(enumm.getName())); } - -@Test - public void testResolve() throws Exception { + + @Test + public void testResolve() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); - - Enum enummDT = (Enum)dataMgr.resolve(enumm, null); + + Enum enummDT = (Enum) dataMgr.resolve(enumm, null); assertNotNull(enummDT); - + long id = dataMgr.getResolvedID(enummDT); - - assertEquals(enummDT, dataMgr.getDataType(id)); + + assertEquals(enummDT, dataMgr.getDataType(id)); } -@Test - public void testReplace() throws Exception { + + @Test + public void testReplace() throws Exception { Enum enumm = new EnumDataType("Color", 1); enumm.add("Red", 10); enumm.add("Green", 15); enumm.add("Blue", 20); Category root = dataMgr.getRootCategory(); Category c = root.createCategory("enumms"); - Enum enummDT = (Enum)c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); - + Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); + Enum myEnum = new EnumDataType("my enumm", 1); myEnum.add("My red", 0); myEnum.add("My Green", 5); @@ -233,39 +220,22 @@ public class EnumTest extends AbstractGenericTest { myEnum.add("Purple", 10); enummDT.replaceWith(myEnum); - + assertEquals(4, enummDT.getCount()); long[] values = enummDT.getValues(); assertEquals(4, values.length); - + assertEquals(0, values[0]); assertEquals(5, values[1]); assertEquals(10, values[2]); assertEquals(25, values[3]); - - try { + + try { enummDT.getValue("Red"); Assert.fail("Should have gotten no such element exception!"); - } catch (NoSuchElementException e) { + } + catch (NoSuchElementException e) { } } -// private class DomainObjListener implements DomainObjectListener { -// private int count; -// -// /* (non-Javadoc) -// * @see ghidra.framework.model.DomainObjectListener#domainObjectChanged(ghidra.framework.model.DomainObjectChangedEvent) -// */ -// public void domainObjectChanged(DomainObjectChangedEvent ev) { -// for (int i=0; i list = new ArrayList(); + ArrayList list = new ArrayList<>(); dtMgr.getAllDataTypes(list); int size = list.size(); @@ -86,8 +82,9 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { "\n"); } - Assert.fail("Did not get exptected data types of byte, Typdef and Typedef. Instead found:\n" + - buffy.toString()); + Assert.fail( + "Did not get exptected data types of byte, Typdef and Typedef. Instead found:\n" + + buffy.toString()); } assertTrue(dt1.isEquivalent(dtMgr.getDataType(CategoryPath.ROOT, "T1"))); @@ -148,7 +145,7 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, false); assertFalse(dtMgr.isUpdatable()); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); dtMgr.getAllDataTypes(list); assertEquals(13, list.size()); diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java index 19a715ca76..b5746ebad9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java @@ -19,18 +19,10 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.program.model.mem.ByteMemBufferImpl; -public class FloatDataTypeTest extends AbstractGenericTest { - - /** - * Constructor for LongDoubleDataTypeTest. - * @param arg0 - */ - public FloatDataTypeTest() { - super(); - } +public class FloatDataTypeTest extends AbstractGTest { private byte[] getBytes(long value, int size) { byte[] bytes = new byte[size]; @@ -41,8 +33,8 @@ public class FloatDataTypeTest extends AbstractGenericTest { return bytes; } -@Test - public void testFloat4Extremes() { + @Test + public void testFloat4Extremes() { int bits = Float.floatToRawIntBits(Float.NaN); byte[] bytes = getBytes(bits, 4); @@ -94,8 +86,8 @@ public class FloatDataTypeTest extends AbstractGenericTest { } -@Test - public void testFloat8Extremes() { + @Test + public void testFloat8Extremes() { long bits = Double.doubleToRawLongBits(Double.NaN); byte[] bytes = getBytes(bits, 8); diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java index 0e6f4d7a74..718024b2d1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java @@ -19,44 +19,24 @@ import static org.junit.Assert.*; import org.junit.*; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.docking.settings.SettingsImpl; -public class FunctionDefinitionDataTypeTest extends AbstractGenericTest { +public class FunctionDefinitionDataTypeTest extends AbstractGTest { private StandAloneDataTypeManager dtm; private FunctionDefinition functionDt; - private int transactionID; private FunctionDefinition createFunctionDefinition(String name) { return (FunctionDefinition) dtm.resolve(new FunctionDefinitionDataType(name), null); } - /** - * Constructor for FunctionDefinitionDataTypeTest. - */ - public FunctionDefinitionDataTypeTest() { - super(); - } - - /* - * @see TestCase#setUp() - */ @Before public void setUp() throws Exception { dtm = new StandAloneDataTypeManager("dummyDTM"); - transactionID = dtm.startTransaction(""); + dtm.startTransaction(""); functionDt = createFunctionDefinition("Test"); } - /* - * @see TestCase#tearDown() - */ - @After - public void tearDown() throws Exception { - dtm.endTransaction(transactionID, true); - dtm.close(); - } - @Test public void testConstructor_WithName() { FunctionDefinitionDataType impl; diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/StructureTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/StructureTest.java deleted file mode 100644 index 07264feb08..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/StructureTest.java +++ /dev/null @@ -1,969 +0,0 @@ -/* ### - * 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.program.model.data; - -import static org.junit.Assert.*; - -import org.junit.*; - -import generic.test.AbstractGTest; - -/** - * - */ -public class StructureTest extends AbstractGTest { - private Structure struct; - - /** - * @param arg0 - */ - public StructureTest() { - super(); - } - - private Structure createStructure(String name, int length) { - return new StructureDataType(name, length); - } - - private Union createUnion(String name) { - return new UnionDataType(name); - } - - private TypeDef createTypeDef(DataType dataType) { - return new TypedefDataType(dataType.getName() + "TypeDef", dataType); - } - - private Array createArray(DataType dataType, int numElements) { - return new ArrayDataType(dataType, numElements, dataType.getLength()); - } - - private Pointer createPointer(DataType dataType, int length) { - return new PointerDataType(dataType, length); - } - - @Before - public void setUp() throws Exception { - struct = createStructure("TestStruct", 0); - struct.add(new ByteDataType(), "field1", "Comment1"); - struct.add(new WordDataType(), null, "Comment2"); - struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); - } - - @Test - public void testAdd() throws Exception { - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); - assertEquals(null, dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(3, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testAdd2() throws Exception { - struct = createStructure("Test", 10); - - assertEquals(10, struct.getLength()); - assertEquals(10, struct.getNumComponents()); - - struct.add(new ByteDataType(), "field1", "Comment1"); - struct.add(new WordDataType(), null, "Comment2"); - struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); - - assertEquals(18, struct.getLength()); - assertEquals(14, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field_0x0", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertNull(dtc.getComment()); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - - assertEquals(null, dtc.getComment()); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - - dtc = struct.getComponent(10); - assertEquals(10, dtc.getOffset()); - assertEquals(10, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(11); - assertEquals(11, dtc.getOffset()); - assertEquals(11, dtc.getOrdinal()); - assertEquals("field_0xb", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(12); - assertEquals(13, dtc.getOffset()); - assertEquals(12, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testInsert_beginning() { - struct.insert(0, new FloatDataType()); - assertEquals(12, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field_0x0", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertNull(dtc.getComment()); - assertEquals(FloatDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(4, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(5, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertEquals("field_0x5", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(4); - assertEquals(11, dtc.getOffset()); - assertEquals(4, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testInsert_end() { - - struct.insert(4, new FloatDataType()); - assertEquals(12, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(3, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(4); - assertEquals(8, dtc.getOffset()); - assertEquals(4, dtc.getOrdinal()); - assertEquals("field_0x8", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(FloatDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testInsert_middle() { - - struct.insert(2, new FloatDataType()); - assertEquals(12, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(0, dtc.getOffset()); - assertEquals(0, dtc.getOrdinal()); - assertEquals("field1", dtc.getFieldName()); - assertEquals("Comment1", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(1); - assertEquals(1, dtc.getOffset()); - assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertEquals("Comment2", dtc.getComment()); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(2); - assertEquals(3, dtc.getOffset()); - assertEquals(2, dtc.getOrdinal()); - assertEquals("field_0x3", dtc.getDefaultFieldName()); - assertNull(dtc.getFieldName()); - assertNull(dtc.getComment()); - assertEquals(FloatDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(3); - assertEquals(7, dtc.getOffset()); - assertEquals(3, dtc.getOrdinal()); - assertEquals("field3", dtc.getFieldName()); - assertEquals(null, dtc.getComment()); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - - dtc = struct.getComponent(4); - assertEquals(11, dtc.getOffset()); - assertEquals(4, dtc.getOrdinal()); - assertEquals("field4", dtc.getFieldName()); - assertEquals("Comment4", dtc.getComment()); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - - } - - @Test - public void testInsertWithEmptySpace() { - struct = createStructure("Test", 100); - struct.insert(40, new ByteDataType()); - struct.insert(20, new WordDataType()); - - struct.insert(10, new FloatDataType()); - - assertEquals(107, struct.getLength()); - assertEquals(103, struct.getNumComponents()); - - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(3, comps.length); - - assertEquals(10, comps[0].getOffset()); - assertEquals(10, comps[0].getOrdinal()); - assertEquals(FloatDataType.class, comps[0].getDataType().getClass()); - - assertEquals(24, comps[1].getOffset()); - assertEquals(21, comps[1].getOrdinal()); - assertEquals(WordDataType.class, comps[1].getDataType().getClass()); - - assertEquals(46, comps[2].getOffset()); - assertEquals(42, comps[2].getOrdinal()); - assertEquals(ByteDataType.class, comps[2].getDataType().getClass()); - } - - // test inserting at offset 0 - @Test - public void testInsertAtOffset() { - struct.insertAtOffset(0, new FloatDataType(), 4); - assertEquals(12, struct.getLength()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - - assertEquals(5, comps.length); - - assertEquals(0, comps[0].getOffset()); - assertEquals(0, comps[0].getOrdinal()); - assertEquals(FloatDataType.class, comps[0].getDataType().getClass()); - - assertEquals(4, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - assertEquals(ByteDataType.class, comps[1].getDataType().getClass()); - - assertEquals(5, comps[2].getOffset()); - assertEquals(2, comps[2].getOrdinal()); - assertEquals(WordDataType.class, comps[2].getDataType().getClass()); - - assertEquals(7, comps[3].getOffset()); - assertEquals(3, comps[3].getOrdinal()); - assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); - - } - - // test inserting at offset 1 - @Test - public void testInsertAtOffset1() { - struct.insertAtOffset(1, new FloatDataType(), 4); - assertEquals(12, struct.getLength()); - - DataTypeComponent[] comps = struct.getDefinedComponents(); - - assertEquals(5, comps.length); - - assertEquals(0, comps[0].getOffset()); - assertEquals(0, comps[0].getOrdinal()); - assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); - - assertEquals(1, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); - - assertEquals(5, comps[2].getOffset()); - assertEquals(2, comps[2].getOrdinal()); - assertEquals(WordDataType.class, comps[2].getDataType().getClass()); - - assertEquals(7, comps[3].getOffset()); - assertEquals(3, comps[3].getOrdinal()); - assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); - - } - - // test inserting at offset 1 - @Test - public void testInsertAtOffset2() { - struct.insertAtOffset(2, new FloatDataType(), 4); - assertEquals(13, struct.getLength()); - - DataTypeComponent[] comps = struct.getDefinedComponents(); - - assertEquals(5, comps.length); - - assertEquals(0, comps[0].getOffset()); - assertEquals(0, comps[0].getOrdinal()); - assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); - - assertEquals(2, comps[1].getOffset()); - assertEquals(2, comps[1].getOrdinal()); - assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); - - assertEquals(6, comps[2].getOffset()); - assertEquals(3, comps[2].getOrdinal()); - assertEquals(WordDataType.class, comps[2].getDataType().getClass()); - - assertEquals(8, comps[3].getOffset()); - assertEquals(4, comps[3].getOrdinal()); - assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); - - } - - @Test - public void testInsertAtOffsetPastEnd() { - struct.insertAtOffset(100, new FloatDataType(), 4); - assertEquals(104, struct.getLength()); - } - - @Test - public void testClearComponent() { - struct.clearComponent(0); - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - DataTypeComponent dtc = struct.getComponent(0); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - dtc = struct.getComponent(1); - assertEquals(WordDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testClearComponent1() { - struct.clearComponent(1); - assertEquals(8, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent dtc = struct.getComponent(1); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - dtc = struct.getComponent(2); - assertEquals(DataType.DEFAULT, dtc.getDataType()); - dtc = struct.getComponent(0); - assertEquals(ByteDataType.class, dtc.getDataType().getClass()); - dtc = struct.getComponent(3); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - assertEquals(3, dtc.getOrdinal()); - assertEquals(3, dtc.getOffset()); - } - - @Test - public void testReplace() { // bigger, no space below - try { - struct.replace(0, new QWordDataType(), 8); - Assert.fail(); - } - catch (Exception e) { - } - } - - @Test - public void testReplace1() { // bigger, space below - struct.insert(1, new QWordDataType()); - struct.clearComponent(1); - assertEquals(16, struct.getLength()); - assertEquals(12, struct.getNumComponents()); - - struct.replace(0, new QWordDataType(), 8); - assertEquals(16, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(9, comps[1].getOffset()); - assertEquals(2, comps[1].getOrdinal()); - } - - @Test - public void testReplace2() { // same size - struct.replace(0, new CharDataType(), 1); - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(CharDataType.class, comps[0].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - } - - @Test - public void testReplace3() { // smaller - struct.replace(1, new CharDataType(), 1); - assertEquals(8, struct.getLength()); - assertEquals(5, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(CharDataType.class, comps[1].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - assertEquals(1, comps[1].getOrdinal()); - assertEquals(3, comps[2].getOffset()); - assertEquals(3, comps[2].getOrdinal()); - assertEquals(DWordDataType.class, comps[2].getDataType().getClass()); - } - - @Test - public void testDelete() { - struct.delete(1); - assertEquals(6, struct.getLength()); - assertEquals(3, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - } - - @Test - public void testDeleteAtOffset() { - struct.deleteAtOffset(2); - assertEquals(6, struct.getLength()); - assertEquals(3, struct.getNumComponents()); - DataTypeComponent[] comps = struct.getDefinedComponents(); - assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); - assertEquals(1, comps[1].getOffset()); - } - - @Test - public void testDeleteAll() { - - Structure s = new StructureDataType("test1", 0); - s.add(new ByteDataType()); - s.add(new FloatDataType()); - - struct.add(s); - s.deleteAll(); - assertEquals(1, s.getLength()); - assertTrue(s.isNotYetDefined()); - assertEquals(0, s.getNumComponents()); - } - - @Test - public void testGetComponents() { - struct = createStructure("Test", 8); - struct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); - struct.insert(5, new WordDataType(), 2, null, "Comment2"); - struct.insert(7, new DWordDataType(), 4, "field8", null); - assertEquals(15, struct.getLength()); - assertEquals(11, struct.getNumComponents()); - DataTypeComponent[] dtcs = struct.getComponents(); - assertEquals(11, dtcs.length); - int offset = 0; - for (int i = 0; i < 11; i++) { - assertEquals(i, dtcs[i].getOrdinal()); - assertEquals(offset, dtcs[i].getOffset()); - offset += dtcs[i].getLength(); - } - assertEquals(DataType.DEFAULT, dtcs[0].getDataType()); - assertEquals(DataType.DEFAULT, dtcs[1].getDataType()); - assertEquals(ByteDataType.class, dtcs[2].getDataType().getClass()); - assertEquals(DataType.DEFAULT, dtcs[3].getDataType()); - assertEquals(DataType.DEFAULT, dtcs[4].getDataType()); - assertEquals(WordDataType.class, dtcs[5].getDataType().getClass()); - assertEquals(DataType.DEFAULT, dtcs[6].getDataType()); - assertEquals(DWordDataType.class, dtcs[7].getDataType().getClass()); - assertEquals(DataType.DEFAULT, dtcs[8].getDataType()); - assertEquals(DataType.DEFAULT, dtcs[9].getDataType()); - assertEquals(DataType.DEFAULT, dtcs[10].getDataType()); - } - - @Test - public void testGetDefinedComponents() { - struct = createStructure("Test", 8); - struct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); - struct.insert(5, new WordDataType(), 2, null, "Comment2"); - struct.insert(7, new DWordDataType(), 4, "field8", null); - assertEquals(15, struct.getLength()); - assertEquals(11, struct.getNumComponents()); - DataTypeComponent[] dtcs = struct.getDefinedComponents(); - assertEquals(3, dtcs.length); - - assertEquals(ByteDataType.class, dtcs[0].getDataType().getClass()); - assertEquals(2, dtcs[0].getOrdinal()); - assertEquals(2, dtcs[0].getOffset()); - - assertEquals(WordDataType.class, dtcs[1].getDataType().getClass()); - assertEquals(5, dtcs[1].getOrdinal()); - assertEquals(5, dtcs[1].getOffset()); - - assertEquals(DWordDataType.class, dtcs[2].getDataType().getClass()); - assertEquals(7, dtcs[2].getOrdinal()); - assertEquals(8, dtcs[2].getOffset()); - - } - - @Test - public void testGetComponentAt() { - DataTypeComponent dtc = struct.getComponentAt(4); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - assertEquals(2, dtc.getOrdinal()); - assertEquals(3, dtc.getOffset()); - } - - @Test - public void testGetDataTypeAt() { - Structure s1 = createStructure("Test1", 0); - s1.add(new WordDataType()); - s1.add(struct); - s1.add(new ByteDataType()); - - DataTypeComponent dtc = s1.getComponentAt(7); - assertEquals(struct, dtc.getDataType()); - dtc = s1.getDataTypeAt(7); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - } - - @Test - public void testReplaceWith() { - assertEquals(8, struct.getLength()); - assertEquals(4, struct.getNumComponents()); - - Structure newStruct = createStructure("Replaced", 8); - newStruct.setDescription("testReplaceWith()"); - DataTypeComponent dtc0 = newStruct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); - DataTypeComponent dtc1 = newStruct.insert(5, new WordDataType(), 2, null, "Comment2"); - DataTypeComponent dtc2 = newStruct.insert(7, new DWordDataType(), 4, "field8", null); - - struct.replaceWith(newStruct); - assertEquals(15, struct.getLength()); - assertEquals(11, struct.getNumComponents()); - DataTypeComponent[] dtcs = struct.getDefinedComponents(); - assertEquals(3, dtcs.length); - assertEquals(dtc0, dtcs[0]); - assertEquals(dtc1, dtcs[1]); - assertEquals(dtc2, dtcs[2]); - assertEquals("TestStruct", struct.getName()); - assertEquals("", struct.getDescription()); - } - - /** - * Test that a structure can't ... ??? - */ - @Test - public void testCyclingProblem() { - Structure newStruct = createStructure("TestStruct", 80); - newStruct.setDescription("testReplaceWith()"); - newStruct.add(new ByteDataType(), "field0", "Comment1"); - newStruct.add(struct, "field1", null); - newStruct.add(new WordDataType(), null, "Comment2"); - newStruct.add(new DWordDataType(), "field3", null); - - try { - struct.add(newStruct); - Assert.fail(); - } - catch (IllegalArgumentException e) { - } - try { - struct.insert(0, newStruct); - Assert.fail(); - } - catch (IllegalArgumentException e) { - } - try { - struct.replace(0, newStruct, newStruct.getLength()); - Assert.fail(); - } - catch (IllegalArgumentException e) { - } - } - - /** - * Test that a structure can't be added to itself. - */ - @Test - public void testCyclicDependencyProblem1() { - try { - struct.add(struct); - Assert.fail("Shouldn't be able to add a structure to itself."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, struct); - Assert.fail("Shouldn't be able to insert a structure into itself."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, struct, struct.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with the structure itself."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure array can't be added to the same structure. - */ - @Test - public void testCyclicDependencyProblem2() { - Array array = createArray(struct, 3); - try { - struct.add(array); - Assert.fail("Shouldn't be able to add a structure array to the same structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, array); - Assert.fail("Shouldn't be able to insert a structure array into the same structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, array, array.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with an array of the same structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a typedef of a structure can't be added to the structure. - */ - @Test - public void testCyclicDependencyProblem3() { - TypeDef typeDef = createTypeDef(struct); - try { - struct.add(typeDef); - Assert.fail("Shouldn't be able to add a structure typedef to the typedef's structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, typeDef); - Assert.fail( - "Shouldn't be able to insert a structure typedef into the typedef's structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, typeDef, typeDef.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with the structure's typedef."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain another structure that contains it. - */ - @Test - public void testCyclicDependencyProblem4() { - Structure anotherStruct = createStructure("AnotherStruct", 0); - anotherStruct.add(struct); - try { - struct.add(anotherStruct); - Assert.fail( - "Shouldn't be able to add another structure, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, anotherStruct); - Assert.fail( - "Shouldn't be able to insert another structure, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, anotherStruct, anotherStruct.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with another structure which contains this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain another structure that contains a typedef to it. - */ - @Test - public void testCyclicDependencyProblem5() { - Structure anotherStruct = createStructure("AnotherStruct", 0); - TypeDef typeDef = createTypeDef(struct); - anotherStruct.add(typeDef); - try { - struct.add(anotherStruct); - Assert.fail( - "Shouldn't be able to add another structure, which contains a typedef of this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, anotherStruct); - Assert.fail( - "Shouldn't be able to insert another structure, which contains a typedef of this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, anotherStruct, anotherStruct.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with another structure which contains a typedef of this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain a union that contains that structure. - */ - @Test - public void testCyclicDependencyProblem6() { - Union union = createUnion("TestUnion"); - union.add(struct); - try { - struct.add(union); - Assert.fail( - "Shouldn't be able to add a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the struct to itself. - } - try { - struct.insert(0, union); - Assert.fail( - "Shouldn't be able to insert a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the struct to itself. - } - try { - struct.replace(0, union, union.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with a union, which contains this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct to itself. - } - } - - /** - * Test that a structure can't contain a typedef of a union that contains that structure. - */ - @Test - public void testCyclicDependencyProblem7() { - Union union = createUnion("TestUnion"); - union.add(struct); - TypeDef typeDef = createTypeDef(union); - try { - struct.add(typeDef); - Assert.fail( - "Shouldn't be able to add a typedef of a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from adding the union typedef to the struct. - } - try { - struct.insert(0, typeDef); - Assert.fail( - "Shouldn't be able to insert a typedef of a union, which contains this structure, to this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from inserting the union typedef to the struct. - } - try { - struct.replace(0, typeDef, typeDef.getLength()); - Assert.fail( - "Shouldn't be able to replace a structure component with a typedef of a union, which contains this structure."); - } - catch (IllegalArgumentException e) { - // Should get an exception from replacing the struct component with the union typedef. - } - } - - /** - * Test that a structure can contain a pointer in it to the same structure. - */ - @Test - public void testNoCyclicDependencyProblemForStructurePointer() { - Pointer structurePointer = createPointer(struct, 4); - try { - struct.add(structurePointer); - } - catch (IllegalArgumentException e) { - Assert.fail("Should be able to add a structure pointer to the pointer's structure."); - } - try { - struct.insert(0, structurePointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to insert a structure pointer into the pointer's structure."); - } - try { - struct.replace(0, structurePointer, structurePointer.getLength()); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to replace a structure component with the structure's pointer."); - } - } - - /** - * Test that a structure can contain a pointer in it to a typedef of the same structure. - */ - @Test - public void testNoCyclicDependencyProblemForTypedefPointer() { - TypeDef typeDef = createTypeDef(struct); - Pointer typedefPointer = createPointer(typeDef, 4); - try { - struct.add(typedefPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to add a structure typedef pointer to the pointer's structure."); - } - try { - struct.insert(0, typedefPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to insert a structure typedef pointer into the pointer's structure."); - } - try { - struct.replace(0, typedefPointer, typedefPointer.getLength()); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to replace a structure component with the structure's typedef pointer."); - } - } - - /** - * Test that a structure can contain a pointer in it to a typedef of the same structure. - */ - @Test - public void testNoCyclicDependencyProblemForArrayPointer() { - TypeDef typeDef = createTypeDef(struct); - Array array = createArray(typeDef, 5); - Pointer arrayPointer = createPointer(array, 4); - try { - struct.add(arrayPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to add a structure typedef array pointer to the pointer's structure."); - } - try { - struct.insert(0, arrayPointer); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to insert a structure typedef arrayointer into the pointer's structure."); - } - try { - struct.replace(0, arrayPointer, arrayPointer.getLength()); - } - catch (IllegalArgumentException e) { - Assert.fail( - "Should be able to replace a structure component with the structure's typedef array pointer."); - } - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/UnionTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/UnionTest.java deleted file mode 100644 index 10efe9d2e1..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/UnionTest.java +++ /dev/null @@ -1,448 +0,0 @@ -/* ### - * 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.program.model.data; - -import static org.junit.Assert.*; - -import org.junit.*; - -import generic.test.AbstractGenericTest; - -/** - * - */ -public class UnionTest extends AbstractGenericTest { - private DataTypeManager dtm; - private Union union; - private int id; - - private Union createUnion(String name) { - Union unionDt = new UnionDataType(name); - return (Union)dtm.addDataType(unionDt,DataTypeConflictHandler.DEFAULT_HANDLER); - } - - private Structure createStructure(String name, int size) { - return (Structure)dtm.addDataType(new StructureDataType(name, size),DataTypeConflictHandler.DEFAULT_HANDLER); - } - - private TypeDef createTypeDef(DataType dataType) { - return new TypedefDataType(dataType.getName()+"TypeDef", dataType); - } - - private Array createArray(DataType dataType, int numElements) { - return new ArrayDataType(dataType, numElements, dataType.getLength()); - } - - private Pointer createPointer(DataType dataType, int length) { - return new PointerDataType(dataType, length); - } - /** - * Constructor for UnionTest. - * @param arg0 - */ - public UnionTest() { - super(); - } - - /* - * @see TestCase#setUp() - */ - @Before - public void setUp() throws Exception { - dtm = new StandAloneDataTypeManager("dummyDTM"); - id = dtm.startTransaction(""); - union = createUnion("TestUnion"); - union.add(new ByteDataType(), "field1", "Comment1"); - union.add(new WordDataType(), null, "Comment2"); - union.add(new DWordDataType(), "field3", null); - union.add(new ByteDataType(), "field4", "Comment4"); - } - - /* - * @see TestCase#tearDown() - */ - @After - public void tearDown() throws Exception { - dtm.endTransaction(id, true); - dtm.close(); - } - -@Test - public void testAdd() throws Exception { - assertEquals(4, union.getLength()); - assertEquals(4, union.getNumComponents()); - - DataTypeComponent[] dtcs = union.getComponents(); - assertEquals(4, dtcs.length); - DataTypeComponent dtc = union.getComponent(3); - assertEquals("field4", dtc.getFieldName()); - assertEquals("byte", dtc.getDataType().getName()); - - Structure struct = new StructureDataType("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - - union.add(struct); - assertEquals(struct.getLength(), union.getLength()); - } -@Test - public void testAdd2() { - Structure struct = createStructure("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - - union.add(struct); - union.delete(4); - assertEquals(4, union.getNumComponents()); - assertEquals(4, union.getLength()); - } - -@Test - public void testGetComponent() { - Structure struct = createStructure("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - DataTypeComponent newdtc = union.add(struct, "field5", "comments"); - DataTypeComponent dtc = union.getComponent(4); - assertEquals(newdtc, dtc); - assertEquals("field5", dtc.getFieldName()); - assertEquals("comments", dtc.getComment()); - - } -@Test - public void testGetComponents() { - Structure struct = createStructure("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - union.add(struct, "field5", "comments"); - DataTypeComponent[] dtcs = union.getComponents(); - assertEquals(5, dtcs.length); - - assertEquals(5, union.getNumComponents()); - } -@Test - public void testInsert() { - Structure struct = createStructure("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - - DataTypeComponent dtc = union.getComponent(2); - assertEquals("field3", dtc.getFieldName()); - - union.insert(2, struct, struct.getLength(), "field5", "field5 comments"); - assertEquals(11, union.getLength()); - dtc = union.getComponent(2); - assertEquals("field5", dtc.getFieldName()); - } -@Test - public void testGetName() { - assertEquals("TestUnion", union.getName()); - } - -@Test - public void testCloneRetainIdentity() throws Exception { - Union unionCopy = (Union)union.clone(null); - assertNull(unionCopy.getDataTypeManager()); - assertEquals(4, union.getLength()); - } - -@Test - public void testCopyDontRetain() throws Exception { - Union unionCopy = (Union)union.copy(null); - assertNull(unionCopy.getDataTypeManager()); - assertEquals(4, union.getLength()); - } - -@Test - public void testDelete() throws Exception { - Structure struct = createStructure("struct_1", 0); - struct.add(new ByteDataType()); - struct.add(new StringDataType(), 10); - union.add(struct); - assertEquals(11, union.getLength()); - - union.delete(4); - assertEquals(4, union.getLength()); - - union.delete(2); - assertEquals(2, union.getLength()); - } - -@Test - public void testIsPartOf() { - Structure struct = createStructure("struct_1", 0); - struct.add(new ByteDataType()); - DataTypeComponent dtc = struct.add(createStructure("mystring", 10)); - DataType dt = dtc.getDataType(); - DataTypeComponent newdtc = union.add(struct); - assertTrue(union.isPartOf(dt)); - - Structure newstruct = (Structure)newdtc.getDataType(); - Structure s1 = (Structure)newstruct.add(createStructure("s1", 1)).getDataType(); - dt = s1.add(new QWordDataType()).getDataType(); - - assertTrue(union.isPartOf(dt)); - } - -@Test - public void testReplaceWith() { - assertEquals(4, union.getLength()); - assertEquals(4, union.getNumComponents()); - - Union newUnion = createUnion("Replaced"); - newUnion.setDescription("testReplaceWith()"); - DataTypeComponent dtc2 = newUnion.insert(0, new DWordDataType(), 4, "field2", null); - DataTypeComponent dtc1 = newUnion.insert(0, new WordDataType(), 2, null, "Comment2"); - DataTypeComponent dtc0 = newUnion.insert(0, new ByteDataType(), 1, "field0", "Comment1"); - - union.replaceWith(newUnion); - assertEquals(4, union.getLength()); - assertEquals(3, union.getNumComponents()); - DataTypeComponent[] dtcs = union.getComponents(); - assertEquals(3, dtcs.length); - assertEquals(dtc0, dtcs[0]); - assertEquals(dtc1, dtcs[1]); - assertEquals(dtc2, dtcs[2]); - assertEquals("TestUnion", union.getName()); - assertEquals("testReplaceWith()", union.getDescription()); - } - -@Test - public void testCyclingProblem() { - Union newUnion = createUnion("Test"); - newUnion.setDescription("testReplaceWith()"); - newUnion.add(new ByteDataType(), "field0", "Comment1"); - newUnion.add(union, "field1", null); - newUnion.add(new WordDataType(), null, "Comment2"); - newUnion.add(new DWordDataType(), "field3", null); - - try { - union.add(newUnion); - Assert.fail(); - } catch (IllegalArgumentException e) { - } - try { - union.insert(0, newUnion); - Assert.fail(); - } catch (IllegalArgumentException e) { - } - } - - /** - * Test that a structure can't be added to itself. - */ -@Test - public void testCyclicDependencyProblem1() { - try { - union.add(union); - Assert.fail("Shouldn't be able to add a union to itself."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the union to itself. - } - try { - union.insert(0, union); - Assert.fail("Shouldn't be able to insert a union into itself."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the union to itself. - } - } - - /** - * Test that a structure array can't be added to the same structure. - */ -@Test - public void testCyclicDependencyProblem2() { - Array array = createArray(union, 3); - try { - union.add(array); - Assert.fail("Shouldn't be able to add a union array to the same union."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the union to itself. - } - try { - union.insert(0, array); - Assert.fail("Shouldn't be able to insert a union array into the same union."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the union to itself. - } - } - - /** - * Test that a typedef of a union can't be added to the union. - */ -@Test - public void testCyclicDependencyProblem3() { - TypeDef typeDef = createTypeDef(union); - try { - union.add(typeDef); - Assert.fail("Shouldn't be able to add a union typedef to the typedef's union."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the union to itself. - } - try { - union.insert(0, typeDef); - Assert.fail("Shouldn't be able to insert a union typedef into the typedef's union."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the union to itself. - } - } - - /** - * Test that a union can't contain another union that contains it. - */ -@Test - public void testCyclicDependencyProblem4() { - Union anotherUnion = createUnion("AnotherUnion"); - anotherUnion.add(union); - try { - union.add(anotherUnion); - Assert.fail("Shouldn't be able to add another union, which contains this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the union to itself. - } - try { - union.insert(0, anotherUnion); - Assert.fail("Shouldn't be able to insert another union, which contains this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the union to itself. - } - } - - /** - * Test that a union can't contain another union that contains a typedef to it. - */ -@Test - public void testCyclicDependencyProblem5() { - Union anotherUnion = createUnion("AnotherUnion"); - TypeDef typeDef = createTypeDef(union); - anotherUnion.add(typeDef); - try { - union.add(anotherUnion); - Assert.fail("Shouldn't be able to add another union, which contains a typedef of this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the union to itself. - } - try { - union.insert(0, anotherUnion); - Assert.fail("Shouldn't be able to insert another union, which contains a typedef of this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the union to itself. - } - } - - /** - * Test that a union can't contain a structure that contains that union. - */ -@Test - public void testCyclicDependencyProblem6() { - Union structure = createUnion("TestStructure"); - structure.add(union); - try { - union.add(structure); - Assert.fail("Shouldn't be able to add a structure, which contains this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the union to itself. - } - try { - union.insert(0, structure); - Assert.fail("Shouldn't be able to insert a structure, which contains this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the union to itself. - } - } - - /** - * Test that a structure can't contain a typedef of a union that contains that structure. - */ -@Test - public void testCyclicDependencyProblem7() { - Structure structure = createStructure("TestStructure", 0); - structure.add(union); - TypeDef typeDef = createTypeDef(structure); - try { - union.add(typeDef); - Assert.fail("Shouldn't be able to add a typedef of a strucutre, which contains this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from adding the structure typedef to the union. - } - try { - union.insert(0, typeDef); - Assert.fail("Shouldn't be able to insert a typedef of a structure, which contains this union, to this union."); - } catch (IllegalArgumentException e) { - // Should get an exception from inserting the structure typedef to the union. - } - } - - /** - * Test that a structure can contain a pointer in it to the same structure. - */ -@Test - public void testNoCyclicDependencyProblemForStructurePointer() { - Pointer unionPointer = createPointer(union, 4); - try { - union.add(unionPointer); - } catch (IllegalArgumentException e) { - Assert.fail("Should be able to add a union pointer to the pointer's union."); - } - try { - union.insert(0, unionPointer); - } catch (IllegalArgumentException e) { - Assert.fail("Should be able to insert a union pointer into the pointer's union."); - } - } - - /** - * Test that a union can contain a pointer in it to a typedef of the same union. - */ -@Test - public void testNoCyclicDependencyProblemForTypedefPointer() { - TypeDef typeDef = createTypeDef(union); - Pointer typedefPointer = createPointer(typeDef, 4); - try { - union.add(typedefPointer); - } catch (IllegalArgumentException e) { - Assert.fail("Should be able to add a union typedef pointer to the pointer's union."); - } - try { - union.insert(0, typedefPointer); - } catch (IllegalArgumentException e) { - Assert.fail("Should be able to insert a union typedef pointer into the pointer's union."); - } - } - - /** - * Test that a union can contain a pointer in it to a typedef of the same union. - */ -@Test - public void testNoCyclicDependencyProblemForArrayPointer() { - TypeDef typeDef = createTypeDef(union); - Array array = createArray(typeDef, 5); - Pointer arrayPointer = createPointer(array, 4); - try { - union.add(arrayPointer); - } catch (IllegalArgumentException e) { - Assert.fail("Should be able to add a union typedef array pointer to the pointer's union."); - } - try { - union.insert(0, arrayPointer); - } catch (IllegalArgumentException e) { - Assert.fail("Should be able to insert a union typedef array pointer into the pointer's union."); - } - } -} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java new file mode 100644 index 0000000000..3c1f602dd4 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java @@ -0,0 +1,91 @@ +/* ### + * 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.program.database.data; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGTest; +import ghidra.program.model.data.*; + +public class BitFieldDBDataTypeTest extends AbstractGTest { + + private DataTypeManager dataMgr; + + @Before + public void setup() throws Exception { + dataMgr = new StandAloneDataTypeManager("dummyDTM"); + dataMgr.startTransaction("Test"); + } + + @Test + public void testGetIdAndGetDataTypeFromId() throws Exception { + + testRoundTrip(new BitFieldDBDataType(CharDataType.dataType, 1, 4, 1, dataMgr)); + testRoundTrip(new BitFieldDBDataType(CharDataType.dataType, 2, 6, 1, dataMgr)); + testRoundTrip(new BitFieldDBDataType(ShortDataType.dataType, 3, 2, 2, dataMgr)); + testRoundTrip(new BitFieldDBDataType(UnsignedShortDataType.dataType, 4, 4, 1, dataMgr)); + testRoundTrip(new BitFieldDBDataType(IntegerDataType.dataType, 5, 7, 2, dataMgr)); + testRoundTrip(new BitFieldDBDataType(UnsignedIntegerDataType.dataType, 14, 2, 3, dataMgr)); + testRoundTrip(new BitFieldDBDataType(LongDataType.dataType, 27, 2, 5, dataMgr)); + testRoundTrip(new BitFieldDBDataType(UnsignedLongDataType.dataType, 6, 0, 1, dataMgr)); + testRoundTrip(new BitFieldDBDataType(LongLongDataType.dataType, 6, 2, 2, dataMgr)); + testRoundTrip(new BitFieldDBDataType(UnsignedLongLongDataType.dataType, 6, 2, 1, dataMgr)); + + // non-standard integer base types + testRoundTrip(new BitFieldDBDataType(ByteDataType.dataType, 6, 2, 1, dataMgr)); + testRoundTrip(new BitFieldDBDataType(QWordDataType.dataType, 6, 2, 1, dataMgr)); + + // TypeDef base types + TypeDef foo = new TypedefDataType("foo", IntegerDataType.dataType); + testRoundTrip(new BitFieldDBDataType(foo, 6, 3, 2, dataMgr)); + + // Enum base types + EnumDataType fum = new EnumDataType("fum", 4); + fum.add("A", 1); + testRoundTrip(new BitFieldDBDataType(fum, 6, 2, 1, dataMgr)); + + } + + private void testRoundTrip(BitFieldDataType packedBitFieldDataType) throws Exception { + + // must resolve first to ensure that TypeDef ID can be encoded within ID, + // otherwise it will be omitted from ID if TypeDef does not already exist + // within corresponding data type manager. + + // This is intended to replicate the order of events when a bit-field + // component is established where the component size corresponds to the + // exact storage size determined by the BitFieldAccumulator during packing + + BitFieldDataType bitFieldDataType = + (BitFieldDataType) dataMgr.resolve(packedBitFieldDataType, null); + + long id = BitFieldDBDataType.getId(bitFieldDataType); + + // The only thing which is preserved is the bitSize, storageSize and bitOffset/Shift + + bitFieldDataType = BitFieldDBDataType.getBitFieldDataType(id, dataMgr); + + assertEquals(packedBitFieldDataType.getBitSize(), bitFieldDataType.getBitSize()); + assertEquals(packedBitFieldDataType.getDeclaredBitSize(), + bitFieldDataType.getDeclaredBitSize()); + assertEquals(packedBitFieldDataType.getBitOffset(), bitFieldDataType.getBitOffset()); + assertEquals(packedBitFieldDataType.getStorageSize(), bitFieldDataType.getStorageSize()); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java new file mode 100644 index 0000000000..171fddbde7 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java @@ -0,0 +1,1878 @@ +/* ### + * 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.program.database.data; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGTest; +import ghidra.program.model.data.*; +import ghidra.util.InvalidNameException; +import ghidra.util.task.TaskMonitor; +import ghidra.util.task.TaskMonitorAdapter; + +public class StructureDBTest extends AbstractGTest { + + private StructureDB struct; + private DataTypeManagerDB dataMgr; + + @Before + public void setUp() throws Exception { + + dataMgr = new StandAloneDataTypeManager("dummyDTM"); + + // default data organization is little-endian + + dataMgr.startTransaction("Test"); + + struct = createStructure("Test", 0); + struct.add(new ByteDataType(), "field1", "Comment1"); + struct.add(new WordDataType(), null, "Comment2"); + struct.add(new DWordDataType(), "field3", null); + struct.add(new ByteDataType(), "field4", "Comment4"); + } + + private void transitionToBigEndian() { + + Structure structClone = (Structure) struct.clone(null); + dataMgr.remove(struct, TaskMonitor.DUMMY); + + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + dataOrg.setBigEndian(true); + + // re-resolve with modified endianess + struct = (StructureDB) dataMgr.resolve(structClone, null); + } + + private StructureDB createStructure(String name, int length) { + return (StructureDB) dataMgr.resolve(new StructureDataType(name, length), null); + } + + private Union createUnion(String name) { + Union unionDt = new UnionDataType(name); + return (Union) dataMgr.addDataType(unionDt, DataTypeConflictHandler.DEFAULT_HANDLER); + } + + private TypeDef createTypeDef(DataType dataType) { + return (TypeDef) dataMgr.resolve( + new TypedefDataType(dataType.getName() + "TypeDef", dataType), null); + } + + private Array createArray(DataType dataType, int numElements) { + return (Array) dataMgr.resolve( + new ArrayDataType(dataType, numElements, dataType.getLength()), null); + } + + private Pointer createPointer(DataType dataType, int length) { + return (Pointer) dataMgr.resolve(new Pointer32DataType(dataType), null); + } + + @Test + public void testAdd() throws Exception { + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumComponents()); + + DataTypeComponent dtc = struct.getComponent(0); + assertEquals(0, dtc.getOffset()); + assertEquals(0, dtc.getOrdinal()); + assertEquals("field1", dtc.getFieldName()); + assertEquals("Comment1", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(1); + assertEquals(1, dtc.getOffset()); + assertEquals(1, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals("Comment2", dtc.getComment()); + assertEquals(WordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(2); + assertEquals(3, dtc.getOffset()); + assertEquals(2, dtc.getOrdinal()); + assertEquals("field3", dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(3); + assertEquals(7, dtc.getOffset()); + assertEquals(3, dtc.getOrdinal()); + assertEquals("field4", dtc.getFieldName()); + assertEquals("Comment4", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + } + + @Test + public void testAdd2() throws Exception { + struct = createStructure("Test", 10); + + assertEquals(10, struct.getLength()); + assertEquals(10, struct.getNumComponents()); + + struct.add(new ByteDataType(), "field1", "Comment1"); + struct.add(new WordDataType(), "field1", "Comment2"); + struct.add(new DWordDataType(), "field3", null); + struct.add(new ByteDataType(), "field4", "Comment4"); + + assertEquals(18, struct.getLength()); + assertEquals(14, struct.getNumComponents()); + + DataTypeComponent dtc = struct.getComponent(0); + assertEquals(0, dtc.getOffset()); + assertEquals(0, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DataType.DEFAULT, dtc.getDataType()); + + dtc = struct.getComponent(1); + assertEquals(1, dtc.getOffset()); + assertEquals(1, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DataType.DEFAULT, dtc.getDataType()); + + dtc = struct.getComponent(10); + assertEquals(10, dtc.getOffset()); + assertEquals(10, dtc.getOrdinal()); + assertEquals("field1", dtc.getFieldName()); + assertEquals("Comment1", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(11); + assertEquals(11, dtc.getOffset()); + assertEquals(11, dtc.getOrdinal()); + assertEquals("field1", dtc.getFieldName()); + assertEquals("Comment2", dtc.getComment()); + assertEquals(WordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(12); + assertEquals(13, dtc.getOffset()); + assertEquals(12, dtc.getOrdinal()); + assertEquals("field3", dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + } + + @Test + public void testInsert_beginning() { + struct.insert(0, new FloatDataType()); + assertEquals(12, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + + DataTypeComponent dtc = struct.getComponent(0); + assertEquals(0, dtc.getOffset()); + assertEquals(0, dtc.getOrdinal()); + assertEquals(null, dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(FloatDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(1); + assertEquals(4, dtc.getOffset()); + assertEquals(1, dtc.getOrdinal()); + assertEquals("field1", dtc.getFieldName()); + assertEquals("Comment1", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(2); + assertEquals(5, dtc.getOffset()); + assertEquals(2, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals("Comment2", dtc.getComment()); + assertEquals(WordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(3); + assertEquals(7, dtc.getOffset()); + assertEquals(3, dtc.getOrdinal()); + assertEquals("field3", dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(4); + assertEquals(11, dtc.getOffset()); + assertEquals(4, dtc.getOrdinal()); + assertEquals("field4", dtc.getFieldName()); + assertEquals("Comment4", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + } + + @Test + public void testInsert_end() { + struct.insert(4, new FloatDataType()); + assertEquals(12, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + + DataTypeComponent dtc = struct.getComponent(0); + assertEquals(0, dtc.getOffset()); + assertEquals(0, dtc.getOrdinal()); + assertEquals("field1", dtc.getFieldName()); + assertEquals("Comment1", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(1); + assertEquals(1, dtc.getOffset()); + assertEquals(1, dtc.getOrdinal()); + assertEquals(null, dtc.getFieldName()); + assertEquals("Comment2", dtc.getComment()); + assertEquals(WordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(2); + assertEquals(3, dtc.getOffset()); + assertEquals(2, dtc.getOrdinal()); + assertEquals("field3", dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(3); + assertEquals(7, dtc.getOffset()); + assertEquals(3, dtc.getOrdinal()); + assertEquals("field4", dtc.getFieldName()); + assertEquals("Comment4", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(4); + assertEquals(8, dtc.getOffset()); + assertEquals(4, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(FloatDataType.class, dtc.getDataType().getClass()); + + } + + @Test + public void testInsert_middle() { + struct.insert(2, new FloatDataType()); + assertEquals(12, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + + DataTypeComponent dtc = struct.getComponent(0); + assertEquals(0, dtc.getOffset()); + assertEquals(0, dtc.getOrdinal()); + assertEquals("field1", dtc.getFieldName()); + assertEquals("Comment1", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(1); + assertEquals(1, dtc.getOffset()); + assertEquals(1, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals("Comment2", dtc.getComment()); + assertEquals(WordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(2); + assertEquals(3, dtc.getOffset()); + assertEquals(2, dtc.getOrdinal()); + assertNull(dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(FloatDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(3); + assertEquals(7, dtc.getOffset()); + assertEquals(3, dtc.getOrdinal()); + assertEquals("field3", dtc.getFieldName()); + assertEquals(null, dtc.getComment()); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + + dtc = struct.getComponent(4); + assertEquals(11, dtc.getOffset()); + assertEquals(4, dtc.getOrdinal()); + assertEquals("field4", dtc.getFieldName()); + assertEquals("Comment4", dtc.getComment()); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + } + + @Test + public void testInsertWithEmptySpace() { + struct = createStructure("Test", 100); + struct.insert(40, new ByteDataType()); + struct.insert(20, new WordDataType()); + + struct.insert(10, new FloatDataType()); + + assertEquals(107, struct.getLength()); + assertEquals(103, struct.getNumComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(3, comps.length); + + assertEquals(10, comps[0].getOffset()); + assertEquals(10, comps[0].getOrdinal()); + assertEquals(FloatDataType.class, comps[0].getDataType().getClass()); + + assertEquals(24, comps[1].getOffset()); + assertEquals(21, comps[1].getOrdinal()); + assertEquals(WordDataType.class, comps[1].getDataType().getClass()); + + assertEquals(46, comps[2].getOffset()); + assertEquals(42, comps[2].getOrdinal()); + assertEquals(ByteDataType.class, comps[2].getDataType().getClass()); + } + + // test inserting at offset 0 + @Test + public void testInsertAtOffset() { + struct.insertAtOffset(0, new FloatDataType(), 4); + assertEquals(12, struct.getLength()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(FloatDataType.class, comps[0].getDataType().getClass()); + + assertEquals(4, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertEquals(ByteDataType.class, comps[1].getDataType().getClass()); + + assertEquals(5, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + assertEquals(7, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); + + } + + // test inserting at offset 1 + @Test + public void testInsertAtOffset1() { + struct.insertAtOffset(1, new FloatDataType(), 4); + assertEquals(12, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); + + assertEquals(5, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + assertEquals(7, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); + + } + + // test inserting at offset 1 + @Test + public void testInsertAtOffset2() { + struct.insertAtOffset(2, new FloatDataType(), 4); + assertEquals(13, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); + + assertEquals(6, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + assertEquals(8, comps[3].getOffset()); + assertEquals(4, comps[3].getOrdinal()); + assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); + + } + + @Test + public void testInsertAtOffsetPastEnd() { + struct.insertAtOffset(100, new FloatDataType(), 4); + assertEquals(104, struct.getLength()); + } + + @Test + public void testSetFlexArray() throws Exception { + + struct.setInternallyAligned(true); + + struct.delete(2); // remove dword to verify flex array alignment below + + struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", "FlexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 byte 1 field4 \"Comment4\"\n" + + " char[0] 0 flex \"FlexComment\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + + struct.setFlexibleArrayComponent(IntegerDataType.dataType, "flex", "FlexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 byte 1 field4 \"Comment4\"\n" + + " int[0] 0 flex \"FlexComment\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testZeroBitFields() throws Exception { + + struct.setInternallyAligned(true); + + struct.delete(2); // remove dword to verify flex array alignment below + + struct.insertBitField(0, 0, 0, IntegerDataType.dataType, 3, "bf2", "bf1Comment"); + struct.insertBitField(0, 0, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + struct.insertBitField(1, 0, 0, IntegerDataType.dataType, 0, "z1", "zero bitfield 1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 4 int:0(0) 1 \"zero bitfield 1\"\n" + + " 4 int:3(0) 1 bf2 \"bf1Comment\"\n" + + " 5 byte 1 field1 \"Comment1\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + + struct.insertBitField(2, 0, 0, IntegerDataType.dataType, 0, "z2", "zero bitfield 2"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 4 int:0(0) 1 \"zero bitfield 1\"\n" + + " 8 int:0(0) 1 \"zero bitfield 2\"\n" + + " 8 int:3(0) 1 bf2 \"bf1Comment\"\n" + + " 9 byte 1 field1 \"Comment1\"\n" + + " 10 word 2 null \"Comment2\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldLittleEndianAppend() throws Exception { + + struct.insertBitField(4, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 undefined 1 null \"\"\n" + + " 11 undefined 1 null \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitField(4, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 8 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 undefined 1 null \"\"\n" + + " 11 undefined 1 null \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitField(6, 1, 0, ByteDataType.dataType, 3, "bf3", "bf3Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 8 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 9 byte:3(0) 1 bf3 \"bf3Comment\"\n" + + " 10 undefined 1 null \"\"\n" + + " 11 undefined 1 null \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldAtLittleEndianAppend() throws Exception { + + struct.insertBitFieldAt(10, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 11 undefined 1 null \"\"\n" + + " 12 undefined 1 null \"\"\n" + + " 13 undefined 1 null \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(10, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 10 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 11 undefined 1 null \"\"\n" + + " 12 undefined 1 null \"\"\n" + + " 13 undefined 1 null \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldAtLittleEndian() throws Exception { + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + try { + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 12, "bf4", "bf4Comment"); + fail( + "expected - IllegalArgumentException: Bitfield does not fit within specified constraints"); + } + catch (IllegalArgumentException e) { + // expected + } + + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 0, "z", "zero bitfield"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:0(0) 1 \"zero bitfield\"\n" + // field name discarded + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertBitFieldAtBigEndian() throws Exception { + + transitionToBigEndian(); + + try { + struct.insertBitFieldAt(2, 4, 30, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + fail( + "expected - IllegalArgumentException: Bitfield does not fit within specified constraints"); + } + catch (IllegalArgumentException e) { + // expected + } + + struct.insertBitFieldAt(2, 4, 29, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 26, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 11, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(3) 3 bf3 \"bf3Comment\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(3) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(0) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.insertBitFieldAt(2, 4, 31, IntegerDataType.dataType, 0, "z", "zero bitfield"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:0(7) 1 \"zero bitfield\"\n" + // field name discarded + " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(3) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(0) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testInsertAtOffsetAfterBeforeBitField() throws Exception { + + // NOTE: little-endian packing applies + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + struct.insertAtOffset(2, FloatDataType.dataType, 4); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 float 4 null \"\"\n" + + " 6 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 6 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 6 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 8 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 10 word 2 null \"Comment2\"\n" + + " 12 dword 4 field3 \"\"\n" + + " 16 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 17 Actual Alignment = 1", struct); + //@formatter:on + + } + + @Test + public void testClearComponent() { + struct.clearComponent(0); + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumComponents()); + DataTypeComponent dtc = struct.getComponent(0); + assertEquals(DataType.DEFAULT, dtc.getDataType()); + dtc = struct.getComponent(1); + assertEquals(WordDataType.class, dtc.getDataType().getClass()); + } + + @Test + public void testClearComponent1() { + struct.clearComponent(1); + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + DataTypeComponent dtc = struct.getComponent(1); + assertEquals(DataType.DEFAULT, dtc.getDataType()); + dtc = struct.getComponent(2); + assertEquals(DataType.DEFAULT, dtc.getDataType()); + dtc = struct.getComponent(0); + assertEquals(ByteDataType.class, dtc.getDataType().getClass()); + dtc = struct.getComponent(3); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + assertEquals(3, dtc.getOrdinal()); + assertEquals(3, dtc.getOffset()); + } + + @Test + public void testReplaceFailure() {// bigger, no space below + DataTypeComponent dtc = null; + try { + dtc = struct.replace(0, new QWordDataType(), 8); + } + catch (IllegalArgumentException e) { + // Not enough undefined bytes so should throw this. + } + assertNull(dtc); + } + + @Test + public void testReplace1() {// bigger, space below + struct.insert(1, new QWordDataType()); + struct.clearComponent(1); + assertEquals(16, struct.getLength()); + assertEquals(12, struct.getNumComponents()); + + struct.replace(0, new QWordDataType(), 8); + assertEquals(16, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(9, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + } + + @Test + public void testReplace2() {// same size + struct.replace(0, new CharDataType(), 1); + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(CharDataType.class, comps[0].getDataType().getClass()); + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + } + + @Test + public void testReplace3() {// smaller + struct.replace(1, new CharDataType(), 1); + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(CharDataType.class, comps[1].getDataType().getClass()); + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertEquals(3, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertEquals(DWordDataType.class, comps[2].getDataType().getClass()); + } + + @Test + public void testDataTypeReplaced1() {// bigger, space below + Structure struct2 = createStructure("struct2", 3); + Structure struct2A = createStructure("struct2A", 5); + struct.insert(0, DataType.DEFAULT); + struct.insert(3, struct2); + struct.clearComponent(4); + assertEquals(12, struct.getLength()); + assertEquals(9, struct.getNumComponents()); + + struct.dataTypeReplaced(struct2, struct2A); + assertEquals(12, struct.getLength()); + assertEquals(7, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(4, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertEquals(5, comps[2].getLength()); + assertEquals(11, comps[3].getOffset()); + assertEquals(6, comps[3].getOrdinal()); + + // check that everything got properly written to the database by + // forcing it to reload + struct.setInvalid(); + + comps = struct.getDefinedComponents(); + assertEquals(4, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertEquals(5, comps[2].getLength()); + assertEquals(11, comps[3].getOffset()); + assertEquals(6, comps[3].getOrdinal()); + + } + + @Test + public void testDataTypeReplaced3() {// bigger, no space at end (structure grows) + Structure struct2 = createStructure("struct2", 3); + Structure struct2A = createStructure("struct2A", 5); + struct.add(struct2); + assertEquals(11, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + + struct.dataTypeReplaced(struct2, struct2A); + assertEquals(13, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(8, comps[4].getOffset()); + assertEquals(4, comps[4].getOrdinal()); + assertEquals(5, comps[4].getLength()); + } + + @Test + public void testDataTypeSizeChanged() { + Structure struct2 = createStructure("struct2", 3); + struct.add(struct2); + assertEquals(11, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + + struct2.add(new WordDataType()); + assertEquals(13, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(8, comps[4].getOffset()); + assertEquals(4, comps[4].getOrdinal()); + assertEquals(5, comps[4].getLength()); + } + + @Test + public void testDataTypeComponentReplaced() {// bigger, no space at end (structure grows) + Structure struct2 = createStructure("struct2", 3); + Structure struct2A = createStructure("struct2A", 5); + struct.add(struct2); + assertEquals(11, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + + struct.replace(4, struct2A, struct2A.getLength()); + assertEquals(13, struct.getLength()); + assertEquals(5, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(8, comps[4].getOffset()); + assertEquals(4, comps[4].getOrdinal()); + assertEquals(5, comps[4].getLength()); + } + + @Test + public void testDataTypeReplaced2() {// smaller, create undefineds + Structure struct2 = createStructure("struct2", 5); + Structure struct2A = createStructure("struct2A", 3); + struct.insert(0, DataType.DEFAULT); + struct.insert(0, DataType.DEFAULT); + struct.insert(4, struct2); + assertEquals(15, struct.getLength()); + assertEquals(7, struct.getNumComponents()); + + struct.dataTypeReplaced(struct2, struct2A); + + assertEquals(15, struct.getLength()); + assertEquals(9, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(5, comps[2].getOffset()); + assertEquals(4, comps[2].getOrdinal()); + assertEquals(3, comps[2].getLength()); + assertEquals(10, comps[3].getOffset()); + assertEquals(7, comps[3].getOrdinal()); + } + + @Test + public void testDeleteFlexArrayDependency() { + + struct.setFlexibleArrayComponent(IntegerDataType.dataType, "flex", "FlexComment"); + + dataMgr.remove(struct.getFlexibleArrayComponent().getDataType(), TaskMonitor.DUMMY); + + assertNull(struct.getFlexibleArrayComponent()); + } + + @Test + public void testDeleteBitFieldDependency() throws InvalidDataTypeException { + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + td = (TypeDef) dataMgr.resolve(td, null); + + struct.addBitField(td, 4, "MyBit1", "bitComment"); + struct.addBitField(td, 3, "MyBit2", "bitComment"); + struct.addBitField(td, 2, "MyBit3", "bitComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 Foo:4(0) 1 MyBit1 \"bitComment\"\n" + + " 9 Foo:3(0) 1 MyBit2 \"bitComment\"\n" + + " 10 Foo:2(0) 1 MyBit3 \"bitComment\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + dataMgr.remove(td, TaskMonitor.DUMMY); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 undefined 1 null \"\"\n" + + " 10 undefined 1 null \"\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testReplaceFlexArrayDependency() throws DataTypeDependencyException { + + struct.setInternallyAligned(true); + + struct.delete(2); // remove dword to verify flex array alignment below + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + td = (TypeDef) dataMgr.resolve(td, null); + + struct.setFlexibleArrayComponent(td, "flex", "FlexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 byte 1 field4 \"Comment4\"\n" + + " Foo[0] 0 flex \"FlexComment\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + + dataMgr.replaceDataType(struct.getFlexibleArrayComponent().getDataType(), + ByteDataType.dataType, false); + + assertNotNull(struct.getFlexibleArrayComponent()); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 byte 1 field4 \"Comment4\"\n" + + " byte[0] 0 flex \"FlexComment\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testReplaceBitFieldDependency() + throws InvalidDataTypeException, DataTypeDependencyException { + + struct.setInternallyAligned(true); + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + td = (TypeDef) dataMgr.resolve(td, null); + + struct.addBitField(td, 4, "MyBit1", "bitComment1"); + struct.addBitField(td, 3, "MyBit2", "bitComment2"); + struct.addBitField(td, 2, "MyBit3", "bitComment3"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 dword 4 field3 \"\"\n" + + " 8 byte 1 field4 \"Comment4\"\n" + + " 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + + dataMgr.replaceDataType(td, CharDataType.dataType, false); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Aligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 dword 4 field3 \"\"\n" + + " 8 byte 1 field4 \"Comment4\"\n" + + " 9 char:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 9 char:3(4) 1 MyBit2 \"bitComment2\"\n" + + " 10 char:2(0) 1 MyBit3 \"bitComment3\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testReplaceWith() throws InvalidDataTypeException { + + // NOTE: unaligned bitfields should remain unchanged when + // transitioning endianess even though it makes little sense. + // Unaligned structures are not intended to be portable! + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + td = (TypeDef) dataMgr.resolve(td, null); + + struct.insertBitFieldAt(9, 1, 0, td, 4, "MyBit1", "bitComment1"); + struct.insertBitFieldAt(9, 1, 4, td, 3, "MyBit2", "bitComment2"); + struct.insertBitFieldAt(9, 2, 7, td, 2, "MyBit3", "bitComment3"); + struct.growStructure(1); + + struct.setFlexibleArrayComponent(td, "myFlex", "flexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + + " 11 undefined 1 null \"\"\n" + + " Foo[0] 0 myFlex \"flexComment\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", struct); + //@formatter:on + + transitionToBigEndian(); + + Structure newStruct = + (Structure) dataMgr.resolve(new StructureDataType("bigStruct", 0), null); + newStruct.replaceWith(struct); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/bigStruct\n" + + "Unaligned\n" + + "Structure bigStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 word 2 null \"Comment2\"\n" + + " 3 dword 4 field3 \"\"\n" + + " 7 byte 1 field4 \"Comment4\"\n" + + " 8 undefined 1 null \"\"\n" + + " 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + + " 11 undefined 1 null \"\"\n" + + " Foo[0] 0 myFlex \"flexComment\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 1", newStruct); + //@formatter:on + } + + @Test + public void testDelete() throws InvalidDataTypeException { + + struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment"); + struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment"); + struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 dword 4 field3 \"\"\n" + + " 12 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 13 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(6); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(3); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(3); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(4); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(2); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 undefined 1 null \"\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 undefined 1 null \"\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(2); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + + "Unaligned\n" + + "Structure Test {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 1 undefined 1 null \"\"\n" + + " 2 undefined 1 null \"\"\n" + + " 3 undefined 1 null \"\"\n" + + " 4 undefined 1 null \"\"\n" + + " 5 dword 4 field3 \"\"\n" + + " 9 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 1", struct); + //@formatter:on + + assertEquals(10, struct.getLength()); + assertEquals(7, struct.getNumComponents()); + assertEquals(3, struct.getNumDefinedComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); + assertEquals(5, comps[1].getOffset()); + } + + @Test + public void testDeleteAtOffset() { + struct.deleteAtOffset(2); + assertEquals(6, struct.getLength()); + assertEquals(3, struct.getNumComponents()); + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); + assertEquals(1, comps[1].getOffset()); + } + + @Test + public void testDeleteComponent() { + Structure s = new StructureDataType("test1", 0); + s.add(new ByteDataType()); + s.add(new FloatDataType()); + + struct.add(s); + + DataType dt = struct.getDataTypeManager().getDataType(struct.getCategoryPath(), "test1"); + assertNotNull(dt); + + DataTypeComponent[] dtc = struct.getComponents(); + assertEquals(5, dtc.length); + + dt.getDataTypeManager().remove(dt, new TaskMonitorAdapter()); + dtc = struct.getComponents(); + assertEquals(9, dtc.length); + + assertEquals(9, struct.getNumComponents()); + } + + @Test + public void testDeleteAll() { + + Structure s = new StructureDataType("test1", 0); + s.add(new ByteDataType()); + s.add(new FloatDataType()); + + struct.add(s); + + DataType dt = struct.getDataTypeManager().getDataType(struct.getCategoryPath(), "test1"); + s = (Structure) dt; + s.deleteAll(); + assertEquals(1, s.getLength()); + assertTrue(s.isNotYetDefined()); + assertEquals(0, s.getNumComponents()); + } + + @Test + public void testGetComponentAt() { + DataTypeComponent dtc = struct.getComponentAt(4); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + assertEquals(2, dtc.getOrdinal()); + assertEquals(3, dtc.getOffset()); + } + + @Test + public void testGetDataTypeAt() { + Structure s1 = createStructure("Test1", 0); + s1.add(new WordDataType()); + s1.add(struct); + s1.add(new ByteDataType()); + + DataTypeComponent dtc = s1.getComponentAt(7); + assertEquals(struct, dtc.getDataType()); + dtc = s1.getDataTypeAt(7); + assertEquals(DWordDataType.class, dtc.getDataType().getClass()); + } + + @Test + public void testAddVarLengthDataTypes() { + Structure s1 = createStructure("Test1", 0); + s1.add(new StringDataType(), 5); + s1.add(new StringDataType(), 10); + s1.add(new StringDataType(), 15); + + DataTypeComponent dtc = s1.getComponentAt(5); + DataType dt = dtc.getDataType(); + assertEquals(-1, dt.getLength()); + assertEquals(10, dtc.getLength()); + assertEquals("string", dt.getDisplayName()); + + } + + @Test + public void testReplaceVarLengthDataTypes() { + Structure s1 = new StructureDataType("Test1", 25); + + s1.replaceAtOffset(0, new StringDataType(), 5, null, null); + s1.replaceAtOffset(5, new StringDataType(), 10, null, null); + s1.replaceAtOffset(15, new StringDataType(), 10, null, null); + + dataMgr.getRootCategory(); + s1 = (Structure) dataMgr.resolve(s1, null); + + DataTypeComponent dtc = s1.getComponentAt(5); + DataType dt = dtc.getDataType(); + assertEquals(-1, dt.getLength()); + assertEquals(10, dtc.getLength()); + assertEquals("string", dt.getDisplayName()); + } + + @Test + public void testReplaceVarLengthDataTypes2() { + + Structure s1 = new StructureDataType("Test1", 0x60); + s1.replaceAtOffset(0, new StringDataType(), 0xd, null, null); + + s1.replaceAtOffset(0xd, new StringDataType(), 0xd, null, null); + s1.replaceAtOffset(0x19, new StringDataType(), 0xc, null, null); + s1.replaceAtOffset(0x24, new StringDataType(), 0xb, null, null); + s1.replaceAtOffset(0x31, new StringDataType(), 0xd, null, null); + s1.replaceAtOffset(0x3e, new StringDataType(), 0xa, null, null); + s1.replaceAtOffset(0x48, new StringDataType(), 0xb, null, null); + s1.replaceAtOffset(0x53, new StringDataType(), 0xd, null, null); + + dataMgr.getRootCategory(); + s1 = (Structure) dataMgr.resolve(s1, null); + + DataTypeComponent dtc = s1.getComponentAt(0); + DataType dt = dtc.getDataType(); + assertEquals("string", dt.getDisplayName()); + assertEquals(0xd, dtc.getLength()); + + dtc = s1.getComponentAt(0x31); + dt = dtc.getDataType(); + assertEquals("string", dt.getDisplayName()); + assertEquals(0xd, dtc.getLength()); + } + + @Test + public void testSetName() throws Exception { + Structure s1 = new StructureDataType("Test1", 0); + s1.add(new StringDataType(), 5); + s1.add(new StringDataType(), 10); + s1.add(new StringDataType(), 15); + s1.add(new ByteDataType()); + + Structure s = (Structure) dataMgr.resolve(s1, null); + s.setName("NewName"); + + assertEquals("NewName", s.getName()); + } + + @Test + public void testTypedefName() throws Exception { + Structure s1 = new StructureDataType("Test1", 0); + s1.add(new StringDataType(), 5); + s1.add(new StringDataType(), 10); + TypedefDataType typdef = new TypedefDataType("TypedefToTest1", s1); + TypeDef td = (TypeDef) dataMgr.resolve(typdef, null); + assertEquals("TypedefToTest1", td.getName()); + + td.setName("NewTypedef"); + assertEquals("NewTypedef", td.getName()); + } + + /** + * Test that a structure can't be added to itself. + */ + @Test + public void testCyclicDependencyProblem1() { + try { + struct.add(struct); + Assert.fail("Shouldn't be able to add a structure to itself."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the struct to itself. + } + try { + struct.insert(0, struct); + Assert.fail("Shouldn't be able to insert a structure into itself."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the struct to itself. + } + try { + struct.replace(0, struct, struct.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with the structure itself."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct to itself. + } + } + + /** + * Test that a structure array can't be added to the same structure. + */ + @Test + public void testCyclicDependencyProblem2() { + Array array = createArray(struct, 3); + try { + struct.add(array); + Assert.fail("Shouldn't be able to add a structure array to the same structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the struct to itself. + } + try { + struct.insert(0, array); + Assert.fail("Shouldn't be able to insert a structure array into the same structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the struct to itself. + } + try { + struct.replace(0, array, array.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with an array of the same structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct to itself. + } + } + + /** + * Test that a typedef of a structure can't be added to the structure. + */ + @Test + public void testCyclicDependencyProblem3() { + TypeDef typeDef = createTypeDef(struct); + try { + struct.add(typeDef); + Assert.fail("Shouldn't be able to add a structure typedef to the typedef's structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the struct to itself. + } + try { + struct.insert(0, typeDef); + Assert.fail( + "Shouldn't be able to insert a structure typedef into the typedef's structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the struct to itself. + } + try { + struct.replace(0, typeDef, typeDef.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with the structure's typedef."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct to itself. + } + } + + /** + * Test that a structure can't contain another structure that contains it. + */ + @Test + public void testCyclicDependencyProblem4() { + Structure anotherStruct = createStructure("AnotherStruct", 0); + anotherStruct.add(struct); + try { + struct.add(anotherStruct); + Assert.fail( + "Shouldn't be able to add another structure, which contains this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the struct to itself. + } + try { + struct.insert(0, anotherStruct); + Assert.fail( + "Shouldn't be able to insert another structure, which contains this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the struct to itself. + } + try { + struct.replace(0, anotherStruct, anotherStruct.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with another structure which contains this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct to itself. + } + } + + /** + * Test that a structure can't contain another structure that contains a typedef to it. + */ + @Test + public void testCyclicDependencyProblem5() { + Structure anotherStruct = createStructure("AnotherStruct", 0); + TypeDef typeDef = createTypeDef(struct); + anotherStruct.add(typeDef); + try { + struct.add(anotherStruct); + Assert.fail( + "Shouldn't be able to add another structure, which contains a typedef of this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the struct to itself. + } + try { + struct.insert(0, anotherStruct); + Assert.fail( + "Shouldn't be able to insert another structure, which contains a typedef of this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the struct to itself. + } + try { + struct.replace(0, anotherStruct, anotherStruct.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with another structure which contains a typedef of this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct to itself. + } + } + + /** + * Test that a structure can't contain a union that contains that structure. + */ + @Test + public void testCyclicDependencyProblem6() { + Union union = createUnion("TestUnion"); + union.add(struct); + try { + struct.add(union); + Assert.fail( + "Shouldn't be able to add a union, which contains this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the struct to itself. + } + try { + struct.insert(0, union); + Assert.fail( + "Shouldn't be able to insert a union, which contains this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the struct to itself. + } + try { + struct.replace(0, union, union.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with a union, which contains this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct to itself. + } + } + + /** + * Test that a structure can't contain a typedef of a union that contains that structure. + */ + @Test + public void testCyclicDependencyProblem7() { + Union union = createUnion("TestUnion"); + union.add(struct); + TypeDef typeDef = createTypeDef(union); + try { + struct.add(typeDef); + Assert.fail( + "Shouldn't be able to add a typedef of a union, which contains this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union typedef to the struct. + } + try { + struct.insert(0, typeDef); + Assert.fail( + "Shouldn't be able to insert a typedef of a union, which contains this structure, to this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union typedef to the struct. + } + try { + struct.replace(0, typeDef, typeDef.getLength()); + Assert.fail( + "Shouldn't be able to replace a structure component with a typedef of a union, which contains this structure."); + } + catch (IllegalArgumentException e) { + // Should get an exception from replacing the struct component with the union typedef. + } + } + + /** + * Test that a structure can contain a pointer in it to the same structure. + */ + @Test + public void testNoCyclicDependencyProblemForStructurePointer() { + Pointer structurePointer = createPointer(struct, 4); + try { + struct.add(structurePointer); + } + catch (IllegalArgumentException e) { + Assert.fail("Should be able to add a structure pointer to the pointer's structure."); + } + try { + struct.insert(0, structurePointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to insert a structure pointer into the pointer's structure."); + } + try { + struct.replace(0, structurePointer, structurePointer.getLength()); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to replace a structure component with the structure's pointer."); + } + } + + /** + * Test that a structure can contain a pointer in it to a typedef of the same structure. + */ + @Test + public void testNoCyclicDependencyProblemForTypedefPointer() { + TypeDef typeDef = createTypeDef(struct); + Pointer typedefPointer = createPointer(typeDef, 4); + try { + struct.add(typedefPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to add a structure typedef pointer to the pointer's structure."); + } + try { + struct.insert(0, typedefPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to insert a structure typedef pointer into the pointer's structure."); + } + try { + struct.replace(0, typedefPointer, typedefPointer.getLength()); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to replace a structure component with the structure's typedef pointer."); + } + } + + /** + * Test that a structure can contain a pointer in it to a typedef of the same structure. + */ + @Test + public void testNoCyclicDependencyProblemForArrayPointer() { + TypeDef typeDef = createTypeDef(struct); + Array array = createArray(typeDef, 5); + Pointer arrayPointer = createPointer(array, 4); + try { + struct.add(arrayPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to add a structure typedef array pointer to the pointer's structure."); + } + try { + struct.insert(0, arrayPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to insert a structure typedef arrayointer into the pointer's structure."); + } + try { + struct.replace(0, arrayPointer, arrayPointer.getLength()); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to replace a structure component with the structure's typedef array pointer."); + } + } + + @Test + public void testNoCyclicDependencyProblemForNestedStruct() { + Structure child = new StructureDataType(struct.getCategoryPath(), struct.getName(), 0); + try { + struct.add(child); + fail( + "Should NOT be able to reference same structure within itself based upon equivelency."); + } + catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testNoCyclicDependencyProblemForNestedStruct2() throws InvalidNameException { + StructureDataType child = new StructureDataType("Foo", 0); + StructureDataType newStruct = new StructureDataType("Tmp", 0); + newStruct.add(child); + newStruct.setName("Foo"); // circumvents cyclic check for StructureDataType + try { + dataMgr.resolve(newStruct, null); + fail( + "Should NOT be able to reference same structure within itself based upon equivelency."); + } + catch (IllegalArgumentException e) { + // expected + } + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java new file mode 100644 index 0000000000..800a76154d --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java @@ -0,0 +1,654 @@ +/* ### + * 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.program.database.data; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGTest; +import ghidra.program.model.data.*; +import ghidra.util.task.TaskMonitor; + +/** + * + */ +public class UnionDBTest extends AbstractGTest { + + private DataTypeManager dataMgr; + private UnionDB union; + + @Before + public void setUp() throws Exception { + + dataMgr = new StandAloneDataTypeManager("dummydataMgr"); + + // default data organization is little-endian + + dataMgr.startTransaction("Test"); + + union = createUnion("TestUnion"); + union.add(new ByteDataType(), "field1", "Comment1"); + union.add(new WordDataType(), null, "Comment2"); + union.add(new DWordDataType(), "field3", null); + union.add(new ByteDataType(), "field4", "Comment4"); + } + + private void transitionToBigEndian() { + + Union unionClone = (Union) union.clone(null); + dataMgr.remove(union, TaskMonitor.DUMMY); + + DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); + dataOrg.setBigEndian(true); + + // re-resolve with modified endianess + union = (UnionDB) dataMgr.resolve(unionClone, null); + } + + private UnionDB createUnion(String name) { + Union unionDt = new UnionDataType(name); + return (UnionDB) dataMgr.addDataType(unionDt, DataTypeConflictHandler.DEFAULT_HANDLER); + } + + private Structure createStructure(String name, int size) { + return (Structure) dataMgr.addDataType(new StructureDataType(name, size), + DataTypeConflictHandler.DEFAULT_HANDLER); + } + + private TypeDef createTypeDef(DataType dataType) { + return new TypedefDataType(dataType.getName() + "TypeDef", dataType); + } + + private Array createArray(DataType dataType, int numElements) { + return new ArrayDataType(dataType, numElements, dataType.getLength()); + } + + private Pointer createPointer(DataType dataType, int length) { + return new PointerDataType(dataType, length); + } + + @Test + public void testAdd() throws Exception { + assertEquals(4, union.getLength()); + assertEquals(4, union.getNumComponents()); + + DataTypeComponent[] dtcs = union.getComponents(); + assertEquals(4, dtcs.length); + DataTypeComponent dtc = union.getComponent(3); + assertEquals("field4", dtc.getFieldName()); + assertEquals("byte", dtc.getDataType().getName()); + + Structure struct = new StructureDataType("struct_1", 0); + struct.add(new ByteDataType()); + struct.add(new StringDataType(), 10); + + union.add(struct); + assertEquals(struct.getLength(), union.getLength()); + } + + @Test + public void testAdd2() { + Structure struct = createStructure("struct_1", 0); + struct.add(new ByteDataType()); + struct.add(new StringDataType(), 10); + + union.add(struct); + union.delete(4); + assertEquals(4, union.getNumComponents()); + assertEquals(4, union.getLength()); + } + + @Test + public void testGetComponent() { + Structure struct = createStructure("struct_1", 0); + struct.add(new ByteDataType()); + struct.add(new StringDataType(), 10); + DataTypeComponent newdtc = union.add(struct, "field5", "comments"); + DataTypeComponent dtc = union.getComponent(4); + assertEquals(newdtc, dtc); + assertEquals("field5", dtc.getFieldName()); + assertEquals("comments", dtc.getComment()); + + } + + @Test + public void testGetComponents() { + Structure struct = createStructure("struct_1", 0); + struct.add(new ByteDataType()); + struct.add(new StringDataType(), 10); + union.add(struct, "field5", "comments"); + DataTypeComponent[] dtcs = union.getComponents(); + assertEquals(5, dtcs.length); + + assertEquals(5, union.getNumComponents()); + } + + @Test + public void testInsert() { + Structure struct = createStructure("struct_1", 0); + struct.add(new ByteDataType()); + struct.add(new StringDataType(), 10); + + DataTypeComponent dtc = union.getComponent(2); + assertEquals("field3", dtc.getFieldName()); + + union.insert(2, struct, struct.getLength(), "field5", "field5 comments"); + assertEquals(11, union.getLength()); + dtc = union.getComponent(2); + assertEquals("field5", dtc.getFieldName()); + } + + @Test + public void testBitFieldUnionLength() throws Exception { + + int cnt = union.getNumComponents(); + for (int i = 0; i < cnt; i++) { + union.delete(0); + } + union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment"); + union.insert(0, ShortDataType.dataType); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 short 2 null \"\"\n" + + " 1 byte:2(4) 1 bf1 \"bf1Comment\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testInsertBitFieldLittleEndian() throws Exception { + + union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); + union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 int:4(0) 1 bf1 \"bf1Comment\"\n" + + " 0 byte:4(0) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testInsertBitFieldBigEndian() throws Exception { + + transitionToBigEndian(); + + union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); + union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 int:4(4) 1 bf1 \"bf1Comment\"\n" + + " 0 byte:4(4) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testDeleteBitFieldDependency() throws InvalidDataTypeException { + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + td = (TypeDef) dataMgr.resolve(td, null); + + union.insertBitField(2, 4, 0, td, 4, "bf1", "bf1Comment"); + union.insertBitField(3, 1, 0, td, 4, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 Foo:4(0) 1 bf1 \"bf1Comment\"\n" + + " 0 Foo:4(0) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + + dataMgr.remove(td, TaskMonitor.DUMMY); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testReplaceBitFieldDependency() + throws InvalidDataTypeException, DataTypeDependencyException { + + TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); + td = (TypeDef) dataMgr.resolve(td, null); + + union.insertBitField(2, 4, 0, td, 4, "bf1", "bf1Comment"); + union.insertBitField(3, 1, 0, td, 4, "bf2", "bf2Comment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 Foo:4(0) 1 bf1 \"bf1Comment\"\n" + + " 0 Foo:4(0) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + + dataMgr.replaceDataType(td, CharDataType.dataType, false); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 char:4(0) 1 bf1 \"bf1Comment\"\n" + + " 0 char:4(0) 1 bf2 \"bf2Comment\"\n" + + " 0 dword 4 field3 \"\"\n" + + " 0 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + } + + @Test + public void testGetName() { + assertEquals("TestUnion", union.getName()); + } + + @Test + public void testCloneRetainIdentity() throws Exception { + Union unionCopy = (Union) union.clone(null); + assertNull(unionCopy.getDataTypeManager()); + assertEquals(4, union.getLength()); + } + + @Test + public void testCopyDontRetain() throws Exception { + Union unionCopy = (Union) union.copy(null); + assertNull(unionCopy.getDataTypeManager()); + assertEquals(4, union.getLength()); + } + + @Test + public void testDelete() throws Exception { + Structure struct = createStructure("struct_1", 0); + struct.add(new ByteDataType()); + struct.add(new StringDataType(), 10); + union.add(struct); + assertEquals(11, union.getLength()); + + union.delete(4); + assertEquals(4, union.getLength()); + + union.delete(2); + assertEquals(2, union.getLength()); + } + + @Test + public void testIsPartOf() { + Structure struct = createStructure("struct_1", 0); + struct.add(new ByteDataType()); + DataTypeComponent dtc = struct.add(createStructure("mystring", 10)); + DataType dt = dtc.getDataType(); + DataTypeComponent newdtc = union.add(struct); + assertTrue(union.isPartOf(dt)); + + Structure newstruct = (Structure) newdtc.getDataType(); + Structure s1 = (Structure) newstruct.add(createStructure("s1", 1)).getDataType(); + dt = s1.add(new QWordDataType()).getDataType(); + + assertTrue(union.isPartOf(dt)); + } + + @Test + public void testReplaceWith() throws InvalidDataTypeException { + assertEquals(4, union.getLength()); + assertEquals(4, union.getNumComponents()); + + Union newUnion = createUnion("Replaced"); + newUnion.setDescription("testReplaceWith()"); + newUnion.insert(0, new DWordDataType(), 4, "field2", null); + newUnion.insert(0, new WordDataType(), 2, null, "Comment2"); + newUnion.insert(0, new ByteDataType(), 1, "field0", "Comment1"); + newUnion.addBitField(IntegerDataType.dataType, 4, "MyBit1", "bitComment1"); + newUnion.addBitField(IntegerDataType.dataType, 3, "MyBit2", "bitComment2"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Replaced\n" + + "Unaligned\n" + + "Union Replaced {\n" + + " 0 byte 1 field0 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 dword 4 field2 \"\"\n" + + " 0 int:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 0 int:3(0) 1 MyBit2 \"bitComment2\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", newUnion); + //@formatter:on + + union.replaceWith(newUnion); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + + "Unaligned\n" + + "Union TestUnion {\n" + + " 0 byte 1 field0 \"Comment1\"\n" + + " 0 word 2 null \"Comment2\"\n" + + " 0 dword 4 field2 \"\"\n" + + " 0 int:4(0) 1 MyBit1 \"bitComment1\"\n" + + " 0 int:3(0) 1 MyBit2 \"bitComment2\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", union); + //@formatter:on + + assertEquals("", union.getDescription()); // unchanged + } + + @Test + public void testCyclingProblem() { + Union newUnion = createUnion("Test"); + newUnion.setDescription("testReplaceWith()"); + newUnion.add(new ByteDataType(), "field0", "Comment1"); + newUnion.add(union, "field1", null); + newUnion.add(new WordDataType(), null, "Comment2"); + newUnion.add(new DWordDataType(), "field3", null); + + try { + union.add(newUnion); + Assert.fail(); + } + catch (IllegalArgumentException e) { + // expected + } + try { + union.insert(0, newUnion); + Assert.fail(); + } + catch (IllegalArgumentException e) { + // expected + } + } + + /** + * Test that a structure can't be added to itself. + */ + @Test + public void testCyclicDependencyProblem1() { + try { + union.add(union); + Assert.fail("Shouldn't be able to add a union to itself."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union to itself. + } + try { + union.insert(0, union); + Assert.fail("Shouldn't be able to insert a union into itself."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union to itself. + } + } + + /** + * Test that a structure array can't be added to the same structure. + */ + @Test + public void testCyclicDependencyProblem2() { + Array array = createArray(union, 3); + try { + union.add(array); + Assert.fail("Shouldn't be able to add a union array to the same union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union to itself. + } + try { + union.insert(0, array); + Assert.fail("Shouldn't be able to insert a union array into the same union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union to itself. + } + } + + /** + * Test that a typedef of a union can't be added to the union. + */ + @Test + public void testCyclicDependencyProblem3() { + TypeDef typeDef = createTypeDef(union); + try { + union.add(typeDef); + Assert.fail("Shouldn't be able to add a union typedef to the typedef's union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union to itself. + } + try { + union.insert(0, typeDef); + Assert.fail("Shouldn't be able to insert a union typedef into the typedef's union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union to itself. + } + } + + /** + * Test that a union can't contain another union that contains it. + */ + @Test + public void testCyclicDependencyProblem4() { + Union anotherUnion = createUnion("AnotherUnion"); + anotherUnion.add(union); + try { + union.add(anotherUnion); + Assert.fail( + "Shouldn't be able to add another union, which contains this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union to itself. + } + try { + union.insert(0, anotherUnion); + Assert.fail( + "Shouldn't be able to insert another union, which contains this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union to itself. + } + } + + /** + * Test that a union can't contain another union that contains a typedef to it. + */ + @Test + public void testCyclicDependencyProblem5() { + Union anotherUnion = createUnion("AnotherUnion"); + TypeDef typeDef = createTypeDef(union); + anotherUnion.add(typeDef); + try { + union.add(anotherUnion); + Assert.fail( + "Shouldn't be able to add another union, which contains a typedef of this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union to itself. + } + try { + union.insert(0, anotherUnion); + Assert.fail( + "Shouldn't be able to insert another union, which contains a typedef of this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union to itself. + } + } + + /** + * Test that a union can't contain a structure that contains that union. + */ + @Test + public void testCyclicDependencyProblem6() { + Union structure = createUnion("TestStructure"); + structure.add(union); + try { + union.add(structure); + Assert.fail( + "Shouldn't be able to add a structure, which contains this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the union to itself. + } + try { + union.insert(0, structure); + Assert.fail( + "Shouldn't be able to insert a structure, which contains this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the union to itself. + } + } + + /** + * Test that a structure can't contain a typedef of a union that contains that structure. + */ + @Test + public void testCyclicDependencyProblem7() { + Structure structure = createStructure("TestStructure", 0); + structure.add(union); + TypeDef typeDef = createTypeDef(structure); + try { + union.add(typeDef); + Assert.fail( + "Shouldn't be able to add a typedef of a strucutre, which contains this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from adding the structure typedef to the union. + } + try { + union.insert(0, typeDef); + Assert.fail( + "Shouldn't be able to insert a typedef of a structure, which contains this union, to this union."); + } + catch (IllegalArgumentException e) { + // Should get an exception from inserting the structure typedef to the union. + } + } + + /** + * Test that a structure can contain a pointer in it to the same structure. + */ + @Test + public void testNoCyclicDependencyProblemForStructurePointer() { + Pointer unionPointer = createPointer(union, 4); + try { + union.add(unionPointer); + } + catch (IllegalArgumentException e) { + Assert.fail("Should be able to add a union pointer to the pointer's union."); + } + try { + union.insert(0, unionPointer); + } + catch (IllegalArgumentException e) { + e.printStackTrace(); + Assert.fail("Should be able to insert a union pointer into the pointer's union."); + } + } + + /** + * Test that a union can contain a pointer in it to a typedef of the same union. + */ + @Test + public void testNoCyclicDependencyProblemForTypedefPointer() { + TypeDef typeDef = createTypeDef(union); + Pointer typedefPointer = createPointer(typeDef, 4); + try { + union.add(typedefPointer); + } + catch (IllegalArgumentException e) { + Assert.fail("Should be able to add a union typedef pointer to the pointer's union."); + } + try { + union.insert(0, typedefPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to insert a union typedef pointer into the pointer's union."); + } + } + + /** + * Test that a union can contain a pointer in it to a typedef of the same union. + */ + @Test + public void testNoCyclicDependencyProblemForArrayPointer() { + TypeDef typeDef = createTypeDef(union); + Array array = createArray(typeDef, 5); + Pointer arrayPointer = createPointer(array, 4); + try { + union.add(arrayPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to add a union typedef array pointer to the pointer's union."); + } + try { + union.insert(0, arrayPointer); + } + catch (IllegalArgumentException e) { + Assert.fail( + "Should be able to insert a union typedef array pointer into the pointer's union."); + } + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java new file mode 100644 index 0000000000..42f1f0e828 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java @@ -0,0 +1,193 @@ +/* ### + * 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.program.model.data; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import generic.test.AbstractGTest; +import ghidra.docking.settings.*; +import ghidra.program.model.mem.ByteMemBufferImpl; +import ghidra.program.model.mem.MemBuffer; +import ghidra.program.model.scalar.Scalar; + +public class BitFieldDataTypeTest extends AbstractGTest { + + @Test + public void testGetBaseSize() throws Exception { + assertEquals(1, new BitFieldDataType(CharDataType.dataType, 1).getBaseTypeSize()); + assertEquals(1, new BitFieldDataType(UnsignedCharDataType.dataType, 1).getBaseTypeSize()); + assertEquals(2, new BitFieldDataType(ShortDataType.dataType, 1).getBaseTypeSize()); + assertEquals(2, new BitFieldDataType(UnsignedShortDataType.dataType, 1).getBaseTypeSize()); + assertEquals(4, new BitFieldDataType(IntegerDataType.dataType, 1).getBaseTypeSize()); + assertEquals(4, + new BitFieldDataType(UnsignedIntegerDataType.dataType, 1).getBaseTypeSize()); + } + + @Test + public void testGetName() throws Exception { + assertEquals("char:1", new BitFieldDataType(CharDataType.dataType, 1).getName()); + assertEquals("uchar:2", new BitFieldDataType(UnsignedCharDataType.dataType, 2).getName()); + assertEquals("short:3", new BitFieldDataType(ShortDataType.dataType, 3).getName()); + assertEquals("ushort:4", new BitFieldDataType(UnsignedShortDataType.dataType, 4).getName()); + assertEquals("int:5", new BitFieldDataType(IntegerDataType.dataType, 5).getName()); + assertEquals("uint:6", new BitFieldDataType(UnsignedIntegerDataType.dataType, 6).getName()); + } + + @Test + public void testGetBaseDataType() throws Exception { + assertEquals(new CharDataType(), + new BitFieldDataType(CharDataType.dataType, 1).getBaseDataType()); + assertEquals(new UnsignedCharDataType(), + new BitFieldDataType(UnsignedCharDataType.dataType, 1).getBaseDataType()); + assertEquals(new ShortDataType(), + new BitFieldDataType(ShortDataType.dataType, 1).getBaseDataType()); + assertEquals(new UnsignedShortDataType(), + new BitFieldDataType(UnsignedShortDataType.dataType, 1).getBaseDataType()); + assertEquals(new IntegerDataType(), + new BitFieldDataType(IntegerDataType.dataType, 1).getBaseDataType()); + assertEquals(new UnsignedIntegerDataType(), + new BitFieldDataType(UnsignedIntegerDataType.dataType, 1).getBaseDataType()); + + DataType typeDef = new TypedefDataType("Foo", LongDataType.dataType); + BitFieldDataType bf = new BitFieldDataType(typeDef, 1); + assertEquals(typeDef, bf.getBaseDataType()); + assertEquals(typeDef, bf.clone(null).getBaseDataType()); + } + + @Test + public void testClone() throws Exception { + + BitFieldDataType bf = new BitFieldDataType(UnsignedIntegerDataType.dataType, 1); + assertEquals(UnsignedIntegerDataType.dataType, bf.getBaseDataType()); + BitFieldDataType bfClone = bf.clone(null); + assertEquals(UnsignedIntegerDataType.dataType, bfClone.getBaseDataType()); + assertEquals(bfClone, bf); + + DataType typeDef = new TypedefDataType("Foo", LongDataType.dataType); + bf = new BitFieldDataType(typeDef, 1); + assertEquals(typeDef, bf.getBaseDataType()); + bfClone = bf.clone(null); + assertEquals(typeDef, bfClone.getBaseDataType()); + assertEquals(bfClone, bf); + } + + @Test + public void testGetBitSize() throws Exception { + assertEquals(1, new BitFieldDataType(CharDataType.dataType, 1).getBitSize()); + assertEquals(2, new BitFieldDataType(UnsignedCharDataType.dataType, 2).getBitSize()); + assertEquals(3, new BitFieldDataType(ShortDataType.dataType, 3).getBitSize()); + assertEquals(4, new BitFieldDataType(UnsignedShortDataType.dataType, 4).getBitSize()); + assertEquals(5, new BitFieldDataType(IntegerDataType.dataType, 5).getBitSize()); + assertEquals(6, new BitFieldDataType(UnsignedIntegerDataType.dataType, 6).getBitSize()); + } + +// @Test +// public void testGetBitOffset() throws Exception { +// assertEquals(0, +// new BitFieldDataType(IntegerDataType.dataType, 1, null).deriveBitField(0, 4, 1, 4).getBitOffset()); +// assertEquals(1, +// new BitFieldDataType(IntegerDataType.dataType, 2, null).deriveBitField(1, 4, 1, 4).getBitOffset()); +// assertEquals(2, +// new BitFieldDataType(IntegerDataType.dataType, 3, null).deriveBitField(2, 4, 1, 4).getBitOffset()); +// assertEquals(3, +// new BitFieldDataType(IntegerDataType.dataType, 4, null).deriveBitField(3, 4, 1, 4).getBitOffset()); +// assertEquals(4, +// new BitFieldDataType(IntegerDataType.dataType, 5, null).deriveBitField(4, 4, 1, 4).getBitOffset()); +// assertEquals(5, +// new BitFieldDataType(IntegerDataType.dataType, 6, null).deriveBitField(5, 4, 1, 4).getBitOffset()); +// } + + @Test + public void testGetValueWithSignedBaseType() throws Exception { + assertEquals(-1, getValue(bitField(1, 0), 0x55)); + assertEquals(0, getValue(bitField(1, 1), 0x55)); + assertEquals(1, getValue(bitField(2, 0), 0x55)); + assertEquals(-3, getValue(bitField(3, 0), 0x55)); + assertEquals(5, getValue(bitField(4, 0), 0x55)); + } + + @Test + public void testGetValueWithUnsignedBaseType() throws Exception { + assertEquals(1, getValue(unsignedBitField(1, 0), 0x55)); + assertEquals(0, getValue(unsignedBitField(1, 1), 0x55)); + assertEquals(1, getValue(unsignedBitField(2, 0), 0x55)); + assertEquals(5, getValue(unsignedBitField(3, 0), 0x55)); + assertEquals(5, getValue(unsignedBitField(4, 0), 0x55)); + } + + @Test + public void testHexRepresentationSignedBaseType() throws Exception { + assertEquals("1h", getRepresentation(bitField(1, 0), 0x55)); + assertEquals("0h", getRepresentation(bitField(1, 1), 0x55)); + assertEquals("1h", getRepresentation(bitField(2, 0), 0x55)); + assertEquals("5h", getRepresentation(bitField(3, 0), 0x55)); + assertEquals("5h", getRepresentation(bitField(4, 0), 0x55)); + } + + @Test + public void testDecimalRepresentationSignedBaseType() throws Exception { + assertEquals("-1", getDecimalRepresentation(bitField(1, 0), 0x55)); + assertEquals("0", getDecimalRepresentation(bitField(1, 1), 0x55)); + assertEquals("1", getDecimalRepresentation(bitField(2, 0), 0x55)); + assertEquals("-3", getDecimalRepresentation(bitField(3, 0), 0x55)); + assertEquals("5", getDecimalRepresentation(bitField(4, 0), 0x55)); + } + + @Test + public void testDecimalRepresentationUnsignedBaseType() throws Exception { + assertEquals("1", getDecimalRepresentation(unsignedBitField(1, 0), 0x55)); + assertEquals("0", getDecimalRepresentation(unsignedBitField(1, 1), 0x55)); + assertEquals("1", getDecimalRepresentation(unsignedBitField(2, 0), 0x55)); + assertEquals("5", getDecimalRepresentation(unsignedBitField(3, 0), 0x55)); + assertEquals("5", getDecimalRepresentation(unsignedBitField(4, 0), 0x55)); + } + + private String getRepresentation(BitFieldDataType bitField, int... bytes) throws Exception { + MemBuffer membuf = membuf(bytes); + return bitField.getRepresentation(membuf, null, 4); + } + + private String getDecimalRepresentation(BitFieldDataType bitField, int... bytes) + throws Exception { + MemBuffer membuf = membuf(bytes); + Settings settings = new SettingsImpl(); + FormatSettingsDefinition.DEF_DECIMAL.setDisplayChoice(settings, "decimal"); + return bitField.getRepresentation(membuf, settings, bitField.getStorageSize()); + } + + private int getValue(BitFieldDataType bitField, int... bytes) throws Exception { + MemBuffer membuf = membuf(bytes); + Scalar scalar = (Scalar) bitField.getValue(membuf, null, bitField.getStorageSize()); + return (int) scalar.getValue(); + } + + private BitFieldDataType bitField(int size, int offset) throws Exception { + int storageSize = BitFieldDataType.getMinimumStorageSize(size); + return new BitFieldDataType(IntegerDataType.dataType, size, offset, storageSize); + } + + private BitFieldDataType unsignedBitField(int size, int offset) throws Exception { + int storageSize = BitFieldDataType.getMinimumStorageSize(size); + return new BitFieldDataType(UnsignedIntegerDataType.dataType, size, offset, storageSize); + } + + private MemBuffer membuf(int... bytes) throws Exception { + return new ByteMemBufferImpl(null, true, bytes); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java index a2f4a2803e..852aaa87a2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CategoryPathTest.java @@ -28,16 +28,12 @@ import java.util.List; import org.junit.Test; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; /** * {@link CategoryPath} tests. */ -public class CategoryPathTest extends AbstractGenericTest { - - public CategoryPathTest() { - super(); - } +public class CategoryPathTest extends AbstractGTest { @Test public void testEscapeStringEmpty() { diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CompositeTestUtils.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CompositeTestUtils.java new file mode 100644 index 0000000000..95bdb56187 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CompositeTestUtils.java @@ -0,0 +1,123 @@ +/* ### + * 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.program.model.data; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.*; + +import ghidra.util.Msg; + +public class CompositeTestUtils { + + private CompositeTestUtils() { + // no construct + } + + /** + * Compare dump of composite with the expected on + * @param test JUnit test instance used for reporting + * @param expectedDump expected composite dump + * @param composite test result composite + */ + public static void assertExpectedComposite(Object test, String expectedDump, + DataType composite) { + assertExpectedComposite(test, expectedDump, composite, false); + } + + /** + * Compare dump of composite with the expected on + * @param test JUnit test instance used for reporting + * @param expectedDump expected composite dump + * @param composite test result composite + * @param recursive if true all child composites will be included in dump + */ + public static void assertExpectedComposite(Object test, String expectedDump, DataType composite, + boolean recursive) { + assertTrue(composite instanceof Composite); + String result = dump(composite, recursive).trim(); + expectedDump = expectedDump.trim(); + int len = expectedDump.length(); + int expectedLine = 1; + int expectedCol = 0; + int index = 0; + boolean mismatch = false; + + for (index = 0; index < len; index++) { + ++expectedCol; + char expectedChar = expectedDump.charAt(index); + if (expectedChar == '\n') { + ++expectedLine; + expectedCol = 0; // newline + } + char resultChar = (index < result.length()) ? result.charAt(index) : 0; + if (resultChar != expectedChar) { + Msg.error(test, "Expected and result differ: expected line " + expectedLine + + ", column " + expectedCol); + mismatch = true; + break; + } + } + + mismatch |= len != result.length(); + + if (mismatch) { + Msg.error(test, "Expected composite:\n" + expectedDump); + Msg.error(test, "Result composite:\n" + result); + fail("Failed to parse expected composite (see log)"); + } + } + + private static Comparator NAME_COMPARATOR = + (o1, o2) -> o1.getPathName().compareTo(o2.getPathName()); + + /** + * Dump composite details for examination or test comparison. + * @param dt composite datatype + * @param recursive if true all child composites will also be dumped recursively + * @return dump string + */ + public static String dump(DataType dt, boolean recursive) { + if (!(dt instanceof Composite)) { + return ""; + } + + Composite composite = (Composite) dt; + StringBuilder buf = new StringBuilder(); + buf.append(composite.toString()); + + if (recursive) { + TreeSet otherComposites = new TreeSet<>(NAME_COMPARATOR); + collectComposites(composite, otherComposites); + for (Composite child : otherComposites) { + buf.append(child.toString()); + } + } + return buf.toString(); + } + + private static void collectComposites(Composite composite, Set collection) { + for (DataTypeComponent c : composite.getComponents()) { + DataType dt = c.getDataType(); + if (dt instanceof Composite) { + Composite childComposite = (Composite) dt; + collection.add(childComposite); + collectComposites(childComposite, collection); + } + } + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataOrganizationTestUtils.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataOrganizationTestUtils.java new file mode 100644 index 0000000000..70ee03a02c --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataOrganizationTestUtils.java @@ -0,0 +1,112 @@ +/* ### + * 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.program.model.data; + +/** + * DataOrganizationTestUtils provides various methods for modifying + * a DataOrganization to reflect a specific processor/compiler spec. This can be used + * when only the DataOrganization is needed and not the Language/CompilerSpec. + */ +public class DataOrganizationTestUtils { + + private DataOrganizationTestUtils() { + // no construct + } + + /** + * Initialize data organization to reflect x86-64-win.cspec specification + * @param dataOrg data organization + */ + public static void initDataOrganizationWindows64BitX86(DataOrganizationImpl dataOrg) { + + dataOrg.setBigEndian(false); + + dataOrg.setAbsoluteMaxAlignment(0); + dataOrg.setMachineAlignment(2); + dataOrg.setDefaultAlignment(1); + dataOrg.setDefaultPointerAlignment(8); + dataOrg.setPointerSize(8); + dataOrg.setWideCharSize(2); + dataOrg.setShortSize(2); + dataOrg.setIntegerSize(4); + dataOrg.setLongSize(4); + dataOrg.setLongLongSize(8); + dataOrg.setFloatSize(4); + dataOrg.setDoubleSize(8); + dataOrg.setLongDoubleSize(8); + dataOrg.setSizeAlignment(1, 1); + dataOrg.setSizeAlignment(2, 2); + dataOrg.setSizeAlignment(4, 4); + dataOrg.setSizeAlignment(8, 8); + + BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); + bitFieldPacking.setUseMSConvention(true); + dataOrg.setBitFieldPacking(bitFieldPacking); + } + + /** + * Initialize data organization to reflect x86-64-gcc.cspec specification + * @param dataOrg data organization + */ + public static void initDataOrganizationGcc64BitX86(DataOrganizationImpl dataOrg) { + + dataOrg.setBigEndian(false); + + dataOrg.setAbsoluteMaxAlignment(0); + dataOrg.setMachineAlignment(2); + dataOrg.setDefaultAlignment(1); + dataOrg.setDefaultPointerAlignment(8); + dataOrg.setPointerSize(8); + dataOrg.setWideCharSize(4); + dataOrg.setShortSize(2); + dataOrg.setIntegerSize(4); + dataOrg.setLongSize(8); + dataOrg.setLongLongSize(8); + dataOrg.setFloatSize(4); + dataOrg.setDoubleSize(8); + dataOrg.setLongDoubleSize(16); + dataOrg.setSizeAlignment(1, 1); + dataOrg.setSizeAlignment(2, 2); + dataOrg.setSizeAlignment(4, 4); + dataOrg.setSizeAlignment(8, 8); + + BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); // use defaults + dataOrg.setBitFieldPacking(bitFieldPacking); + } + + /** + * Initialize data organization to reflect big-endian mips32.cspec specification (MIPS 32-bit) + * @param dataOrg data organization + */ + public static void initDataOrganization32BitMips(DataOrganizationImpl dataOrg) { + + dataOrg.setBigEndian(true); + + dataOrg.setPointerSize(4); + + dataOrg.setFloatSize(4); + dataOrg.setDoubleSize(8); + dataOrg.setLongDoubleSize(8); + + dataOrg.setSizeAlignment(1, 1); + dataOrg.setSizeAlignment(2, 2); + dataOrg.setSizeAlignment(4, 4); + dataOrg.setSizeAlignment(8, 8); + + BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); // use defaults + dataOrg.setBitFieldPacking(bitFieldPacking); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataUtilitiesTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataUtilitiesTest.java index f38b011fd9..5961eedda4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataUtilitiesTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DataUtilitiesTest.java @@ -15,17 +15,18 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import org.junit.*; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.program.model.ProgramTestDouble; import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.model.mem.*; -public class DataUtilitiesTest extends AbstractGenericTest { +public class DataUtilitiesTest extends AbstractGTest { private Program program; private AddressSpace space; @@ -47,11 +48,6 @@ public class DataUtilitiesTest extends AbstractGenericTest { memory = new MyMemory(); } - @After - public void tearDown() throws Exception { - - } - /** * Memory: 0x00 - 0xff * diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java index c59f752024..2ee7d8435b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java @@ -23,9 +23,9 @@ import java.util.Set; import org.junit.Test; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; -public class EnumValuePartitionerTest extends AbstractGenericTest { +public class EnumValuePartitionerTest extends AbstractGTest { @Test public void testDisjointValues() { diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java index 8f19ad0912..6884b47b34 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java @@ -20,22 +20,14 @@ import java.math.BigDecimal; import org.junit.Assert; import org.junit.Test; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.pcode.floatformat.FloatFormat; import ghidra.program.model.mem.ByteMemBufferImpl; -public class Float10DataTypeTest extends AbstractGenericTest { +public class Float10DataTypeTest extends AbstractGTest { - /** - * Constructor for LongDoubleDataTypeTest. - * @param arg0 - */ - public Float10DataTypeTest() { - super(); - } - -@Test - public void testGetValue() { + @Test + public void testGetValue() { byte[] bytes = new byte[] { 0x7f, (byte) 0xff, 0, 0, 0, 0, 0, 0, 0, 0 }; // 0x7fff0000000000000000 = +infinity Object value = diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java index 60f469385b..9b5dd3321a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java @@ -22,7 +22,7 @@ import java.util.Arrays; import org.junit.Test; -import generic.test.AbstractGenericTest; +import generic.test.AbstractGTest; import ghidra.docking.settings.Settings; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.GenericAddressSpace; @@ -30,7 +30,7 @@ import ghidra.program.model.data.RenderUnicodeSettingsDefinition.RENDER_ENUM; import ghidra.program.model.mem.ByteMemBufferImpl; import ghidra.program.model.mem.MemBuffer; -public class StringDataTypeTest extends AbstractGenericTest { +public class StringDataTypeTest extends AbstractGTest { private StringDataType fixedlenString = new StringDataType(); private StringUTF8DataType fixedUtf8String = new StringUTF8DataType(); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java new file mode 100644 index 0000000000..fc086892ad --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java @@ -0,0 +1,398 @@ +/* ### + * 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.program.model.data; + +import java.util.*; + +import ghidra.app.plugin.core.datamgr.archive.SourceArchive; +import ghidra.util.InvalidNameException; +import ghidra.util.UniversalID; +import ghidra.util.task.TaskMonitor; + +/** + * Empty stub of {@link DataTypeManager} + */ +public class TestDummyDataTypeManager implements DataTypeManager { + + @Override + public UniversalID getUniversalID() { + // stub + return null; + } + + @Override + public boolean containsCategory(CategoryPath path) { + // stub + return false; + } + + @Override + public String getUniqueName(CategoryPath path, String baseName) { + // stub + return null; + } + + @Override + public DataType resolve(DataType dataType, DataTypeConflictHandler handler) { + // stub + return null; + } + + @Override + public DataType addDataType(DataType dataType, DataTypeConflictHandler handler) { + // stub + return null; + } + + @Override + public Iterator getAllDataTypes() { + // stub + return null; + } + + @Override + public void getAllDataTypes(List list) { + // stub + + } + + @Override + public Iterator getAllStructures() { + // stub + return null; + } + + @Override + public Iterator getAllComposites() { + // stub + return null; + } + + @Override + public void findDataTypes(String name, List list) { + // stub + + } + + @Override + public void findDataTypes(String name, List list, boolean caseSensitive, + TaskMonitor monitor) { + // stub + + } + + @Override + public DataType replaceDataType(DataType existingDt, DataType replacementDt, + boolean updateCategoryPath) throws DataTypeDependencyException { + // stub + return null; + } + + @Override + public DataType getDataType(String dataTypePath) { + // stub + return null; + } + + @Override + public DataType findDataType(String dataTypePath) { + // stub + return null; + } + + @Override + public DataType getDataType(DataTypePath dataTypePath) { + // stub + return null; + } + + @Override + public long getResolvedID(DataType dt) { + // stub + return 0; + } + + @Override + public long getID(DataType dt) { + // stub + return 0; + } + + @Override + public DataType getDataType(long dataTypeID) { + // stub + return null; + } + + @Override + public Category getCategory(long categoryID) { + // stub + return null; + } + + @Override + public Category getCategory(CategoryPath path) { + // stub + return null; + } + + @Override + public void dataTypeChanged(DataType dataType) { + // stub + + } + + @Override + public void addDataTypeManagerListener(DataTypeManagerChangeListener l) { + // stub + + } + + @Override + public void removeDataTypeManagerListener(DataTypeManagerChangeListener l) { + // stub + + } + + @Override + public void addInvalidatedListener(InvalidatedListener listener) { + // stub + + } + + @Override + public void removeInvalidatedListener(InvalidatedListener listener) { + // stub + + } + + @Override + public boolean remove(DataType dataType, TaskMonitor monitor) { + // stub + return false; + } + + @Override + public boolean contains(DataType dataType) { + // stub + return false; + } + + @Override + public Category createCategory(CategoryPath path) { + // stub + return null; + } + + @Override + public DataType getDataType(CategoryPath path, String name) { + // stub + return null; + } + + @Override + public String getName() { + // stub + return null; + } + + @Override + public void setName(String name) throws InvalidNameException { + // stub + + } + + @Override + public int startTransaction(String description) { + // stub + return 0; + } + + @Override + public boolean isUpdatable() { + // stub + return false; + } + + @Override + public void endTransaction(int transactionID, boolean commit) { + // stub + + } + + @Override + public void flushEvents() { + // stub + + } + + @Override + public void close() { + // stub + + } + + @Override + public Pointer getPointer(DataType datatype) { + // stub + return null; + } + + @Override + public Pointer getPointer(DataType datatype, int size) { + // stub + return null; + } + + @Override + public Category getRootCategory() { + // stub + return null; + } + + @Override + public boolean isFavorite(DataType datatype) { + // stub + return false; + } + + @Override + public void setFavorite(DataType datatype, boolean isFavorite) { + // stub + + } + + @Override + public List getFavorites() { + // stub + return null; + } + + @Override + public int getCategoryCount() { + // stub + return 0; + } + + @Override + public int getDataTypeCount(boolean includePointersAndArrays) { + // stub + return 0; + } + + @Override + public void findEnumValueNames(long value, Set enumValueNames) { + // stub + + } + + @Override + public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID) { + // stub + return null; + } + + @Override + public DataType findDataTypeForID(UniversalID datatypeID) { + // stub + return null; + } + + @Override + public long getLastChangeTimeForMyManager() { + // stub + return 0; + } + + @Override + public SourceArchive getSourceArchive(UniversalID sourceID) { + // stub + return null; + } + + @Override + public ArchiveType getType() { + // stub + return null; + } + + @Override + public List getDataTypes(SourceArchive sourceArchive) { + // stub + return null; + } + + @Override + public SourceArchive getLocalSourceArchive() { + // stub + return null; + } + + @Override + public void associateDataTypeWithArchive(DataType datatype, SourceArchive archive) { + // stub + + } + + @Override + public void disassociate(DataType datatype) { + // stub + + } + + @Override + public boolean updateSourceArchiveName(String archiveFileID, String name) { + // stub + return false; + } + + @Override + public boolean updateSourceArchiveName(UniversalID sourceID, String name) { + // stub + return false; + } + + @Override + public DataOrganization getDataOrganization() { + // stub + return null; + } + + @Override + public List getSourceArchives() { + // stub + return null; + } + + @Override + public void removeSourceArchive(SourceArchive sourceArchive) { + // stub + + } + + @Override + public SourceArchive resolveSourceArchive(SourceArchive sourceArchive) { + // stub + return null; + } + + @Override + public Set getDataTypesContaining(DataType dataType) { + // stub + return null; + } + +} diff --git a/Ghidra/Processors/ARM/data/languages/ARM.cspec b/Ghidra/Processors/ARM/data/languages/ARM.cspec index 351fbeda1a..19c8bb9b00 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM.cspec +++ b/Ghidra/Processors/ARM/data/languages/ARM.cspec @@ -1,6 +1,9 @@ + diff --git a/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec b/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec index 70a9fe89de..db5ca37aae 100644 --- a/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec +++ b/Ghidra/Processors/ARM/data/languages/ARM_v45.cspec @@ -1,10 +1,15 @@ - + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32.cspec b/Ghidra/Processors/MIPS/data/languages/mips32.cspec index 7e0026ead8..2a08a22dc5 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32.cspec @@ -6,6 +6,12 @@ + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec b/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec index ebfce82df1..533906075f 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips32_fp64.cspec @@ -6,6 +6,12 @@ + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64.cspec b/Ghidra/Processors/MIPS/data/languages/mips64.cspec index 417816cf40..a3fe61b44d 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64.cspec @@ -5,6 +5,12 @@ + + + + + + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec b/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec index c930c2952d..70e0c816be 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64_32_n32.cspec @@ -30,7 +30,7 @@ - + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec b/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec index 1283b5fac3..42d9c8fa07 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64_32_o32.cspec @@ -35,7 +35,7 @@ - + diff --git a/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec b/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec index 03be19be7a..128efbb2c6 100644 --- a/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec +++ b/Ghidra/Processors/MIPS/data/languages/mips64_32_o64.cspec @@ -16,6 +16,12 @@ + + + + + + diff --git a/Ghidra/Processors/x86/data/languages/x86-64-win.cspec b/Ghidra/Processors/x86/data/languages/x86-64-win.cspec index c40cfcd3bb..5d060580cd 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64-win.cspec +++ b/Ghidra/Processors/x86/data/languages/x86-64-win.cspec @@ -27,6 +27,9 @@ + + + diff --git a/Ghidra/Processors/x86/data/languages/x86win.cspec b/Ghidra/Processors/x86/data/languages/x86win.cspec index 6019111ce2..29c7bb9228 100644 --- a/Ghidra/Processors/x86/data/languages/x86win.cspec +++ b/Ghidra/Processors/x86/data/languages/x86win.cspec @@ -21,6 +21,9 @@ + + +