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.
-
+
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 # pragmapack(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 extends PdbMember> 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