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 index c374a7a1b0..9f8c2b90e6 100644 --- 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 @@ -23,6 +23,7 @@ import javax.swing.SwingUtilities; import docking.ActionContext; import docking.DockingWindowManager; +import ghidra.program.model.data.Structure; import ghidra.util.exception.AssertException; /** @@ -76,7 +77,8 @@ public class AddBitFieldAction extends CompositeEditorTableAction { public void adjustEnablement() { boolean enabled = true; CompEditorModel editorModel = (CompEditorModel) model; - if (editorModel.viewComposite == null || editorModel.isAligned() || + // Union do not support unaligned placement of bitfields + if (!(editorModel.viewComposite instanceof Structure) || editorModel.isAligned() || editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) { enabled = false; } 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 index f896b76ece..3346ab32d4 100644 --- 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 @@ -20,15 +20,12 @@ import java.awt.event.MouseEvent; import javax.swing.Icon; import javax.swing.JComponent; -import docking.*; +import docking.ActionContext; +import docking.DialogComponentProvider; 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 { @@ -93,7 +90,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider { if (bitfieldDtc == null || !bitFieldEditorPanel.endCurrentEdit()) { return; } - initEdit(bitfieldDtc.getOrdinal()); + initEdit(bitfieldDtc.getOrdinal(), true); } @Override @@ -200,7 +197,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider { initAdd(-editOrdinal - 1); } else { - initEdit(editOrdinal); + initEdit(editOrdinal, false); } return bitFieldEditorPanel; } @@ -234,16 +231,19 @@ public class BitFieldEditorDialog extends DialogComponentProvider { setApplyEnabled(true); } - private void initEdit(int editOrdinal) throws ArrayIndexOutOfBoundsException { + private void initEdit(int editOrdinal, boolean useExistingAllocationSize) + throws ArrayIndexOutOfBoundsException { DataTypeComponent dtc = composite.getComponent(editOrdinal); if (!dtc.isBitFieldComponent()) { throw new IllegalArgumentException("editOrdinal does not correspond to bitfield"); } - bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc)); + bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc), + useExistingAllocationSize); setApplyEnabled(true); } - private int getPreferredAllocationOffset(DataTypeComponent bitfieldDtc) { + static int getPreferredAllocationOffset(DataTypeComponent bitfieldDtc) { + Composite composite = (Composite) bitfieldDtc.getParent(); if (composite instanceof Union) { return 0; } @@ -270,34 +270,4 @@ public class BitFieldEditorDialog extends DialogComponentProvider { 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 index a2b82c3b56..a0cee7a2ab 100644 --- 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 @@ -39,8 +39,8 @@ import ghidra.util.layout.*; import resources.ResourceManager; /** - * BitFieldEditorPanel provides the ability to place bitfields - * within unaligned structures and unions. + * BitFieldEditorPanel provides the ability to add or modify bitfields + * within unaligned structures. */ public class BitFieldEditorPanel extends JPanel { @@ -100,7 +100,7 @@ public class BitFieldEditorPanel extends JPanel { private JPanel createLegendPanel() { JPanel legendPanel = new JPanel(new BorderLayout()); - legendPanel.add(new BitFieldPlacementComponent.BitFieldLegend(), BorderLayout.WEST); + legendPanel.add(new BitFieldPlacementComponent.BitFieldLegend(null), BorderLayout.WEST); return legendPanel; } @@ -390,14 +390,18 @@ public class BitFieldEditorPanel extends JPanel { * If null an allocation size of 4-bytes will be used but may be adjusted. * @param bitfieldDtc bitfield component or null * @param allocationOffset allocation offset to be used + * @param useExistingAllocationSize if true attempt to use existing allocation size */ - void initEdit(DataTypeComponent bitfieldDtc, int allocationOffset) { + void initEdit(DataTypeComponent bitfieldDtc, int allocationOffset, + boolean useExistingAllocationSize) { String initialFieldName = null; DataType initialBaseDataType = null; int allocationSize = -1; - BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); - if (bitFieldAllocation != null) { - allocationSize = bitFieldAllocation.getAllocationByteSize(); + if (useExistingAllocationSize) { + BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); + if (bitFieldAllocation != null) { + allocationSize = bitFieldAllocation.getAllocationByteSize(); + } } if (bitfieldDtc != null) { if (!bitfieldDtc.isBitFieldComponent()) { @@ -420,7 +424,7 @@ public class BitFieldEditorPanel extends JPanel { // TODO: adjust offset and allocationSize if needed placementComponent.setAllocationOffset(allocationOffset); placementComponent.init(allocationSize, bitfieldDtc); - bitFieldAllocation = placementComponent.getBitFieldAllocation(); // get updated instance + BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); // get updated instance initControls(initialFieldName, initialBaseDataType, bitFieldAllocation.getBitSize()); enableControls(bitfieldDtc != null); } 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 index aa56aa5e67..b96c5fe5c4 100644 --- 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 @@ -26,6 +26,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; import ghidra.util.Msg; import ghidra.util.exception.AssertException; +import ghidra.util.layout.VerticalLayout; import resources.icons.ColorIcon; public class BitFieldPlacementComponent extends JPanel { @@ -59,27 +60,50 @@ public class BitFieldPlacementComponent extends JPanel { private EditMode editMode = EditMode.NONE; private int editOrdinal = -1; // FIXME: improve insert use + private DataTypeComponent editComponent; public static class BitFieldLegend extends JPanel { - BitFieldLegend() { - setLayout(new GridLayout(2, 3, 5, 5)); - //setLayout(new RowColumnLayout(10, 10, RowColumnLayout.ROW, 0)); - add(new JLabel("Undefined bits", - new ColorIcon(UNDEFINED_BIT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), - SwingConstants.LEFT)); - add(new JLabel("Defined bitfield", + BitFieldLegend(DataTypeComponent viewedBitfield) { + JPanel legendPanel; + if (viewedBitfield != null) { + setLayout(new VerticalLayout(10)); + legendPanel = new JPanel(new GridLayout(1, 3, 5, 5)); + String viewComponentText = + "Selected bitfield { " + viewedBitfield.getDataType().getDisplayName(); + String viewComponentName = viewedBitfield.getFieldName(); + if (viewComponentName != null) { + viewComponentText += " " + viewComponentName; + } + viewComponentText += " }"; + add(new JLabel(viewComponentText, + new ColorIcon(BITFIELD_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), + SwingConstants.LEFT)); + add(legendPanel); + } + else { + setLayout(new GridLayout(2, 3, 5, 5)); + legendPanel = this; + } + + legendPanel.add(new JLabel("Defined bitfield", new ColorIcon(BITFIELD_COMPONENT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), SwingConstants.LEFT)); - add(new JLabel("Defined non-bitfield", + legendPanel.add(new JLabel("Defined non-bitfield ", new ColorIcon(NON_BITFIELD_COMPONENT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), SwingConstants.LEFT)); - add(new JLabel("Edit bitfield bits", - new ColorIcon(BITFIELD_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), - SwingConstants.LEFT)); - add(new JLabel("Conflict bits", - new ColorIcon(CONFLICT_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), + legendPanel.add(new JLabel("Undefined bits", + new ColorIcon(UNDEFINED_BIT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), SwingConstants.LEFT)); + + if (viewedBitfield == null) { + legendPanel.add(new JLabel("Edit bitfield bits", + new ColorIcon(BITFIELD_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), + SwingConstants.LEFT)); + legendPanel.add(new JLabel("Conflict bits", + new ColorIcon(CONFLICT_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), + SwingConstants.LEFT)); + } } } @@ -127,7 +151,8 @@ public class BitFieldPlacementComponent extends JPanel { } void refresh(int allocationByteSize, int bitSize, int bitOffset) { - bitFieldAllocation = new BitFieldAllocation(allocationByteSize, bitSize, bitOffset); + bitFieldAllocation = + new BitFieldAllocation(allocationByteSize, bitSize, bitOffset, editComponent); updatePreferredSize(); repaint(); } @@ -147,14 +172,16 @@ public class BitFieldPlacementComponent extends JPanel { void initAdd(int allocationByteSize, int bitSize, int bitOffset) { editMode = EditMode.ADD; editOrdinal = -1; + editComponent = null; refresh(allocationByteSize, bitSize, bitOffset); } - void init(int allocationByteSize, DataTypeComponent editComponent) { + void init(int allocationByteSize, DataTypeComponent editDtc) { - if (editComponent == null) { + if (editDtc == null) { editMode = EditMode.NONE; editOrdinal = -1; + this.editComponent = null; refresh(allocationByteSize, 0, 0); return; } @@ -163,12 +190,13 @@ public class BitFieldPlacementComponent extends JPanel { // of the component being modified editMode = EditMode.EDIT; - editOrdinal = editComponent.getOrdinal(); + editOrdinal = editDtc.getOrdinal(); + this.editComponent = editDtc; - BitFieldPlacement placement = new BitFieldPlacement(editComponent, allocationByteSize); + BitFieldPlacement placement = new BitFieldPlacement(editDtc, allocationByteSize); bitFieldAllocation = new BitFieldAllocation(allocationByteSize, placement.rightBit - placement.leftBit + 1, - (8 * allocationByteSize) - placement.rightBit - 1); + (8 * allocationByteSize) - placement.rightBit - 1, editDtc); updatePreferredSize(); repaint(); } @@ -213,6 +241,7 @@ public class BitFieldPlacementComponent extends JPanel { // unexpected removal editMode = EditMode.ADD; editOrdinal = -1; + editComponent = null; } else if (ordinal < editOrdinal) { --editOrdinal; @@ -268,6 +297,7 @@ public class BitFieldPlacementComponent extends JPanel { finally { editMode = EditMode.NONE; editOrdinal = -1; + editComponent = null; bitFieldAllocation.refresh(); repaint(); } @@ -475,18 +505,21 @@ public class BitFieldPlacementComponent extends JPanel { private final int bitSize; private final int bitOffset; private boolean hasConflict; + private DataTypeComponent editComponent; // 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) { + BitFieldAllocation(int allocationByteSize, int bitSize, int bitOffset, + DataTypeComponent editComponent) { if (allocationByteSize <= 0 || (bitSize + bitOffset) > (8 * allocationByteSize)) { throw new IllegalArgumentException("allocation size too small"); } this.allocationByteSize = allocationByteSize; this.bitSize = bitSize; this.bitOffset = bitOffset; + this.editComponent = editComponent; refresh(); } @@ -505,11 +538,11 @@ public class BitFieldPlacementComponent extends JPanel { if (editMode != EditMode.NONE) { int rightMostBit = bitAttributes.length - bitOffset - 1; if (bitSize == 0) { - allocateZeroBitField(null, rightMostBit); + allocateZeroBitField(editComponent, rightMostBit); } else { int leftMostBit = rightMostBit - bitSize + 1; - allocateBits(null, leftMostBit, rightMostBit, false, false); + allocateBits(editComponent, leftMostBit, rightMostBit, false, false); } } @@ -741,7 +774,7 @@ public class BitFieldPlacementComponent extends JPanel { if (zeroBitfield) { return UNDEFINED_BIT_COLOR; } - if (dtc == null) { + if (dtc == editComponent) { return BITFIELD_BITS_COLOR; // edit field } return dtc.isBitFieldComponent() ? BITFIELD_COMPONENT_COLOR diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldViewerDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldViewerDialog.java new file mode 100644 index 0000000000..7c1355f535 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldViewerDialog.java @@ -0,0 +1,56 @@ +/* ### + * 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 javax.swing.JComponent; + +import docking.DialogComponentProvider; +import ghidra.program.model.data.*; + +public class BitFieldViewerDialog extends DialogComponentProvider { + + BitFieldViewerDialog(Composite composite, int editOrdinal) { + super("View " + getCompositeType(composite) + " Bitfield"); + addButtons(); + addWorkPanel(buildWorkPanel(composite, editOrdinal)); + setRememberLocation(false); + setRememberSize(false); + } + + private void addButtons() { + addCancelButton(); + setCancelButtonText("Close"); + } + + private JComponent buildWorkPanel(Composite composite, int viewOrdinal) { + if (viewOrdinal < 0 || viewOrdinal >= composite.getNumComponents()) { + throw new IllegalArgumentException("invalid composite ordinal"); + } + DataTypeComponent dtc = composite.getComponent(viewOrdinal); + if (!dtc.isBitFieldComponent()) { + throw new IllegalArgumentException("editOrdinal does not correspond to bitfield"); + } + return new BitFieldViewerPanel(dtc, BitFieldEditorDialog.getPreferredAllocationOffset(dtc)); + } + + private static String getCompositeType(Composite composite) { + // currently supports unaligned case only! + String alignmentMode = composite.isInternallyAligned() ? "Aligned" : "Unaligned"; + String type = (composite instanceof Union) ? "Union" : "Structure"; + return alignmentMode + " " + type; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldViewerPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldViewerPanel.java new file mode 100644 index 0000000000..fce849d29e --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldViewerPanel.java @@ -0,0 +1,143 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.compositeeditor; + +import java.awt.BorderLayout; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; + +import ghidra.program.model.data.*; +import ghidra.util.layout.*; + +/** + * BitFieldViewerPanel provides the ability to examine bitfield placement + * within structures. + * TODO: consider using as a hover panel + */ +public class BitFieldViewerPanel extends JPanel { + + private Composite composite; + private DataTypeComponent bitfieldDtc; + + private JLabel allocationOffsetLabel; + + private BitFieldPlacementComponent placementComponent; + + BitFieldViewerPanel(DataTypeComponent bitfieldDtc, int allocationOffset) { + super(); + this.bitfieldDtc = bitfieldDtc; + this.composite = (Composite) bitfieldDtc.getParent(); + + setLayout(new VerticalLayout(5)); + setFocusTraversalKeysEnabled(true); + + setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + if (composite instanceof Structure) { + add(createAllocationOffsetPanel()); + } + add(createPlacementPanel()); + add(createLegendPanel()); + initView(allocationOffset); + } + + private JPanel createLegendPanel() { + JPanel legendPanel = new JPanel(new BorderLayout()); + legendPanel.add(new BitFieldPlacementComponent.BitFieldLegend(bitfieldDtc), + BorderLayout.WEST); + return legendPanel; + } + + private JPanel createAllocationOffsetPanel() { + + JPanel panel = new JPanel(new HorizontalLayout(5)); + + allocationOffsetLabel = new JLabel(); + allocationOffsetLabel.setHorizontalTextPosition(SwingConstants.LEFT); + panel.add(allocationOffsetLabel); + + return panel; + } + + private void updateAllocationOffsetLabel() { + if (composite instanceof Structure) { + String text = + "Structure Offset of Allocation Unit: " + placementComponent.getAllocationOffset(); + allocationOffsetLabel.setText(text); + } + } + + 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")); + + JPanel p = new JPanel(new BorderLayout()); + p.add(placementComponent, BorderLayout.WEST); + p.setBorder(new EmptyBorder(0, 0, 5, 0)); + + JScrollPane scrollPane = new JScrollPane(p, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.getViewport().setBackground(getBackground()); + scrollPane.setBorder(null); + + midPanel.add(scrollPane); + return midPanel; + } + + /** + * 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 allocationOffset allocation offset to be used + * @param useExistingAllocationSize if true attempt to use existing allocation size + */ + private void initView(int allocationOffset) { + DataType initialBaseDataType = null; + int allocationSize = -1; + if (bitfieldDtc != null) { + if (!bitfieldDtc.isBitFieldComponent()) { + throw new IllegalArgumentException("unsupport data type component"); + } + BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType(); + initialBaseDataType = bitfieldDt.getBaseDataType(); + if (allocationSize < 1) { + allocationSize = initialBaseDataType.getLength(); + } + int allocationAdjust = composite.getLength() - allocationOffset - allocationSize; + if (allocationAdjust < 0) { + allocationSize += allocationAdjust; + } + } + if (allocationSize < 1) { + allocationSize = 4; + } + + placementComponent.setAllocationOffset(allocationOffset); + placementComponent.init(allocationSize, bitfieldDtc); + updateAllocationOffsetLabel(); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java index 42db763f12..2942245842 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java @@ -1231,4 +1231,11 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen throw new DuplicateNameException("Data type named " + name + " already exists"); } } + + /** + * @return true if presence of bitfields is supported, else false + */ + protected boolean bitfieldsSupported() { + return (viewComposite instanceof Structure) || (viewComposite instanceof Union); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index dd8b28a2d6..e715463b02 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -45,10 +45,7 @@ import docking.widgets.label.GLabel; import docking.widgets.table.GTable; import docking.widgets.table.GTableCellRenderer; import docking.widgets.textfield.GValidatedTextField; -import ghidra.app.plugin.core.data.DataTypeCellRenderer; -import ghidra.app.plugin.core.datamgr.archive.SourceArchive; import ghidra.app.services.DataTypeManagerService; -import ghidra.app.util.ToolTipUtils; import ghidra.app.util.datatype.DataTypeSelectionEditor; import ghidra.app.util.datatype.NavigationDirection; import ghidra.framework.plugintool.Plugin; @@ -144,7 +141,8 @@ public abstract class CompositeEditorPanel extends JPanel private void setupTableCellRenderer() { GTableCellRenderer cellRenderer = new GTableCellRenderer(); - DataTypeCellRenderer dtiCellRenderer = new DataTypeCellRenderer(); + DataTypeCellRenderer dtiCellRenderer = new DataTypeCellRenderer( + model.getOriginalDataTypeManager(), model.bitfieldsSupported()); table.setDefaultRenderer(String.class, cellRenderer); table.setDefaultRenderer(DataTypeInstance.class, dtiCellRenderer); } @@ -558,6 +556,12 @@ public abstract class CompositeEditorPanel extends JPanel table = new CompositeTable(model); table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); table.addMouseListener(new CompositeTableMouseListener()); + if (model.bitfieldsSupported()) { + CompositeTableCellMouseListener cellMouseListener = + new CompositeTableCellMouseListener(); + table.addMouseListener(cellMouseListener); + table.addMouseMotionListener(cellMouseListener); + } CompositeEditorTableAction action = provider.actionMgr.getNamedAction( CompositeEditorTableAction.EDIT_ACTION_PREFIX + EditFieldAction.ACTION_NAME); @@ -1402,6 +1406,85 @@ public abstract class CompositeEditorPanel extends JPanel } } + private class CompositeTableCellMouseListener extends MouseAdapter { + + private boolean trackMovement; + private Cursor originalCursor; + private boolean pointerCursorActive; + + @Override + public void mouseExited(MouseEvent e) { + trackMovement = false; + table.setCursor(originalCursor); + } + + @Override + public void mouseEntered(MouseEvent e) { + trackMovement = true; + originalCursor = table.getCursor(); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (!pointerCursorActive || e.getClickCount() != 1) { + return; + } + Point p = e.getPoint(); + int columnIndex = table.columnAtPoint(p); + int rowIndex = table.rowAtPoint(p); + if ((columnIndex == -1) || (rowIndex == -1)) { + return; + } + DataTypeComponent dtc = model.getComponent(rowIndex); + if (dtc != null && dtc.isBitFieldComponent()) { + e.consume(); + BitFieldViewerDialog dlg = + new BitFieldViewerDialog(model.viewComposite, dtc.getOrdinal()); + Rectangle cellRect = table.getCellRect(rowIndex, columnIndex, false); + Point xyPoint = new Point(cellRect.x + DataTypeCellRenderer.ICON_WIDTH, + cellRect.y + cellRect.height); + SwingUtilities.convertPointToScreen(xyPoint, table); + dlg.setInitialLocation(xyPoint.x, xyPoint.y); + Window w = SwingUtilities.windowForComponent(table); + DockingWindowManager.showDialog(w, dlg, table); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + if (!trackMovement) { + return; + } + Point p = e.getPoint(); + + // Locate the renderer under the event location + int columnIndex = table.columnAtPoint(p); + int rowIndex = table.rowAtPoint(p); + + if ((columnIndex != -1) && (rowIndex != -1) && + DataTypeInstance.class.equals(table.getColumnClass(columnIndex))) { + + DataTypeComponent dtc = model.getComponent(rowIndex); + // ignore non-bitfield rows + if (dtc != null && dtc.isBitFieldComponent()) { + Rectangle cellRect = table.getCellRect(rowIndex, columnIndex, false); + p.translate(-cellRect.x, -cellRect.y); + if (p.x <= (DataTypeCellRenderer.ICON_WIDTH + 2)) { + if (!pointerCursorActive) { + pointerCursorActive = true; + table.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + return; // view bitfield cursor active + } + } + } + if (pointerCursorActive) { + table.setCursor(originalCursor); + pointerCursorActive = false; + } + } + } + private class CompositeTableMouseListener extends MouseAdapter { @Override public void mouseReleased(MouseEvent e) { @@ -1473,69 +1556,6 @@ public abstract class CompositeEditorPanel extends JPanel super(dm); } - // Use the contains method to set the tooltip text depending - // on the table cell the mouse is over. - @Override - public boolean contains(int x, int y) { - if (!super.contains(x, y)) { - return false; - } - Point p = new Point(x, y); - int columnIndex = columnAtPoint(p); - int rowIndex = rowAtPoint(p); - String toolTipText = null; - Object value = model.getValueAt(rowIndex, columnIndex); - if (columnIndex == model.getDataTypeColumn()) { - if (value instanceof DataTypeInstance) { - DataTypeInstance dataTypeInstance = (DataTypeInstance) value; - toolTipText = getDataTypeToolTip(dataTypeInstance.getDataType()); - } - } - else if (value instanceof String) { - String string = (String) value; - if (string.length() == 0) { - string = null; - } - toolTipText = string; - } - String currentToolTipText = getToolTipText(); - if (!SystemUtilities.isEqual(toolTipText, currentToolTipText)) { - setToolTipText(toolTipText); - } - - return true; - } - - private String getDataTypeToolTip(DataType dataType) { - - DataTypeManager dataTypeManager = dataType.getDataTypeManager(); - // This checks for null dataTypeManager below since BadDataType won't have one. - SourceArchive sourceArchive = dataType.getSourceArchive(); - DataTypeManager originalDTM = model.getOriginalDataTypeManager(); - boolean localSource = - (sourceArchive == null) || ((dataTypeManager != null) && SystemUtilities.isEqual( - dataTypeManager.getUniversalID(), sourceArchive.getSourceArchiveID())); - if (localSource) { - sourceArchive = originalDTM.getSourceArchive(originalDTM.getUniversalID()); - } - DataType foundDataType = originalDTM.getDataType(dataType.getDataTypePath()); - - String displayName = ""; - if (foundDataType != null && (dataTypeManager != null)) { - displayName = dataTypeManager.getName(); - } - displayName += dataType.getPathName(); - if (!localSource) { - displayName += " (" + sourceArchive.getName() + ")"; - } - displayName = HTMLUtilities.friendlyEncodeHTML(displayName); - - String toolTipText = ToolTipUtils.getToolTipText(dataType); - String headerText = "" + displayName + "
"; - toolTipText = toolTipText.replace("", headerText); - return toolTipText; - } - @Override // overridden because the editor component was not being given focus public Component prepareEditor(TableCellEditor editor, int row, int column) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeCellRenderer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeCellRenderer.java new file mode 100644 index 0000000000..dd4585445c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeCellRenderer.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.app.plugin.core.compositeeditor; + +import java.awt.Color; +import java.awt.Component; + +import javax.swing.*; + +import docking.widgets.table.GTableCellRenderer; +import docking.widgets.table.GTableCellRenderingData; +import ghidra.app.plugin.core.datamgr.archive.SourceArchive; +import ghidra.app.util.ToolTipUtils; +import ghidra.program.model.data.*; +import ghidra.util.HTMLUtilities; +import ghidra.util.SystemUtilities; +import resources.ResourceManager; +import resources.icons.IconWrapper; +import resources.icons.ScaledImageIconWrapper; + +public class DataTypeCellRenderer extends GTableCellRenderer { + private static final long serialVersionUID = 1L; + + static final int ICON_WIDTH = 12; + static final int ICON_HEIGHT = 12; + + public static final Icon MAGNIFIER_ICON = new IconWrapper() { + @Override + protected Icon createIcon() { + ImageIcon viewBitfieldIcon = ResourceManager.loadImage("images/magnifier.png"); + ScaledImageIconWrapper scaledViewBitfieldIcon = + new ScaledImageIconWrapper(viewBitfieldIcon, ICON_WIDTH, ICON_HEIGHT); + return scaledViewBitfieldIcon; + } + }; + + private DataTypeManager originalDTM; + private boolean includeViewBitfieldIcon; + + public DataTypeCellRenderer(DataTypeManager originalDataTypeManager, + boolean includeViewBitfieldIcon) { + this.originalDTM = originalDataTypeManager; + this.includeViewBitfieldIcon = includeViewBitfieldIcon; + } + + @Override + public Component getTableCellRendererComponent(GTableCellRenderingData data) { + + Object value = data.getValue(); + + String dtString = ""; + String tooltipText = null; + boolean useRed = false; + DataType dt = null; + + if (value instanceof DataTypeInstance) { + dt = ((DataTypeInstance) value).getDataType(); + tooltipText = getDataTypeToolTip(dt); + dtString = dt.getDisplayName(); + if (dt.isNotYetDefined()) { + useRed = true; + } + } + + GTableCellRenderingData renderData = data.copyWithNewValue(dtString); + + JLabel c = (JLabel) super.getTableCellRendererComponent(renderData); + + c.setToolTipText(tooltipText); + + if (useRed) { + c.setForeground(Color.RED); + } + + c.setHorizontalTextPosition(RIGHT); + c.setIcon(null); + + if (includeViewBitfieldIcon && (dt instanceof BitFieldDataType)) { + // add inspect icon and action listener + c.setIcon(MAGNIFIER_ICON); + } + + return c; + } + + private String getDataTypeToolTip(DataType dataType) { + + DataTypeManager dataTypeManager = dataType.getDataTypeManager(); + // This checks for null dataTypeManager below since BadDataType won't have one. + SourceArchive sourceArchive = dataType.getSourceArchive(); + + boolean localSource = (sourceArchive == null) || + ((dataTypeManager != null) && SystemUtilities.isEqual(dataTypeManager.getUniversalID(), + sourceArchive.getSourceArchiveID())); + if (localSource) { + sourceArchive = originalDTM.getSourceArchive(originalDTM.getUniversalID()); + } + + DataType foundDataType = originalDTM.getDataType(dataType.getDataTypePath()); + + String displayName = ""; + if (foundDataType != null && (dataTypeManager != null)) { + displayName = dataTypeManager.getName(); + } + displayName += dataType.getPathName(); + if (!localSource) { + displayName += " (" + sourceArchive.getName() + ")"; + } + displayName = HTMLUtilities.friendlyEncodeHTML(displayName); + + String toolTipText = ToolTipUtils.getToolTipText(dataType); + String headerText = "" + displayName + "
"; + toolTipText = toolTipText.replace("", headerText); + return toolTipText; + } +} 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 83bf377612..8da6fb58a1 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 @@ -71,7 +71,8 @@ public class StructureEditorProvider extends CompositeEditorProvider { new HexNumbersAction(this), new CreateInternalStructureAction(this), new AddBitFieldAction(this), - new EditBitFieldAction(this) + new EditBitFieldAction(this), +// new ViewBitFieldAction(this) }; //@formatter:on } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ViewBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ViewBitFieldAction.java new file mode 100644 index 0000000000..837cfc04ef --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ViewBitFieldAction.java @@ -0,0 +1,87 @@ +/* ### + * 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.SwingUtilities; + +import docking.ActionContext; +import docking.DockingWindowManager; +import ghidra.program.model.data.DataTypeComponent; +import ghidra.program.model.data.Structure; +import ghidra.util.exception.AssertException; + +/** + * Action for use in the composite data type editor. + * This action has help associated with it. + */ +public class ViewBitFieldAction extends CompositeEditorTableAction { + + private final static String ACTION_NAME = "View Bitfield"; + private final static String GROUP_NAME = BITFIELD_ACTION_GROUP; + private final static String DESCRIPTION = "View an existing bitfield"; + private static String[] popupPath = new String[] { ACTION_NAME }; + + public ViewBitFieldAction(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(); + } + + private DataTypeComponent getBitFieldComponent() { + CompEditorModel editorModel = (CompEditorModel) model; + if ((editorModel.viewComposite instanceof Structure) && + editorModel.getNumSelectedRows() == 1) { + int rowIndex = model.getSelectedRows()[0]; + if (rowIndex < model.getNumComponents()) { + DataTypeComponent dtComponent = model.getComponent(rowIndex); + if (dtComponent.isBitFieldComponent()) { + return dtComponent; + } + } + } + return null; + } + + @Override + public void actionPerformed(ActionContext context) { + + CompEditorModel editorModel = (CompEditorModel) model; + + DataTypeComponent dtComponent = getBitFieldComponent(); + if (dtComponent == null) { + return; + } + + BitFieldViewerDialog dlg = + new BitFieldViewerDialog(editorModel.viewComposite, dtComponent.getOrdinal()); + Component c = provider.getComponent(); + Window w = SwingUtilities.windowForComponent(c); + DockingWindowManager.showDialog(w, dlg, c); + requestTableFocus(); + } + + @Override + public void adjustEnablement() { + setEnabled(getBitFieldComponent() != null); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeCellRenderer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeCellRenderer.java deleted file mode 100644 index f9341a6bbc..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeCellRenderer.java +++ /dev/null @@ -1,55 +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.app.plugin.core.data; - -import java.awt.Color; -import java.awt.Component; - -import docking.widgets.table.GTableCellRenderer; -import docking.widgets.table.GTableCellRenderingData; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeInstance; - -public class DataTypeCellRenderer extends GTableCellRenderer { - private static final long serialVersionUID = 1L; - - @Override - public Component getTableCellRendererComponent(GTableCellRenderingData data) { - - Object value = data.getValue(); - - String dtString = ""; - boolean useRed = false; - if (value instanceof DataTypeInstance) { - DataType dt = ((DataTypeInstance) value).getDataType(); - dtString = dt.getDisplayName(); - if (dt.isNotYetDefined()) { - useRed = true; - } - } - - GTableCellRenderingData renderData = data.copyWithNewValue(dtString); - - Component c = - super.getTableCellRendererComponent(renderData); - - if (useRed) { - c.setForeground(Color.RED); - } - - return c; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java index abad2151e3..7e44d6c23e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FindReferencesToFieldAction.java @@ -95,6 +95,9 @@ public class FindReferencesToFieldAction extends DockingAction { DataTypeComponent[] components = composite.getComponents(); List names = new ArrayList<>(); for (DataTypeComponent dataTypeComponent : components) { + if (dataTypeComponent.isBitFieldComponent()) { + continue; + } String fieldName = dataTypeComponent.getFieldName(); if (StringUtils.isBlank(fieldName)) { continue; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java index d46bb45c28..f9f330949c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java @@ -92,6 +92,9 @@ public class ToolTipUtils { else if (dataType instanceof Array) { return new ArrayDataTypeHTMLRepresentation((Array) dataType); } + else if (dataType instanceof BitFieldDataType) { + return new BitFieldDataTypeHTMLRepresentation((BitFieldDataType) dataType); + } else { return new DefaultDataTypeHTMLRepresentation(dataType); } @@ -203,8 +206,9 @@ public class ToolTipUtils { buf.append(""); buf.append(colorString(Color.BLACK, friendlyEncodeHTML(param.getDataType().getName()))); buf.append(""); - Color paramColor = param.getFunction().hasCustomVariableStorage() - ? PARAM_CUSTOM_STORAGE_COLOR : PARAM_DYNAMIC_STORAGE_COLOR; + Color paramColor = + param.getFunction().hasCustomVariableStorage() ? PARAM_CUSTOM_STORAGE_COLOR + : PARAM_DYNAMIC_STORAGE_COLOR; buf.append( colorString(paramColor, friendlyEncodeHTML(param.getVariableStorage().toString()))); buf.append(""); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/BitFieldDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/BitFieldDataTypeHTMLRepresentation.java new file mode 100644 index 0000000000..ca25ffbe37 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/BitFieldDataTypeHTMLRepresentation.java @@ -0,0 +1,63 @@ +/* ### + * 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.html; + +import ghidra.app.util.ToolTipUtils; +import ghidra.program.model.data.BitFieldDataType; +import ghidra.program.model.data.DataType; +import ghidra.util.HTMLUtilities; +import ghidra.util.exception.AssertException; + +public class BitFieldDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation { + + public BitFieldDataTypeHTMLRepresentation(BitFieldDataType bitFieldDt) { + super(buildHTMLText(bitFieldDt)); + } + + private static String buildHTMLText(BitFieldDataType bitFieldDt) { + StringBuffer buffer = new StringBuffer(); + String description = bitFieldDt.getDescription(); + + if (description == null || description.length() == 0) { + description = bitFieldDt.getDisplayName(); + } + description = HTMLUtilities.friendlyEncodeHTML(description); + buffer.append(description); + + DataType baseDataType = bitFieldDt.getBaseDataType(); + + buffer.append(BR).append(BR); + buffer.append("Bitfield Base Data Type: ").append(BR); + + buffer.append(INDENT_OPEN); + HTMLDataTypeRepresentation representation = + ToolTipUtils.getHTMLRepresentation(baseDataType); + String baseHTML = representation.getHTMLContentString(); + buffer.append(baseHTML); + if (baseHTML.indexOf(LENGTH_PREFIX) < 0) { + addDataTypeLength(baseDataType, buffer); + } + buffer.append(INDENT_CLOSE); + + return buffer.toString(); + } + + @Override + public HTMLDataTypeRepresentation[] diff(HTMLDataTypeRepresentation otherRepresentation) { + throw new AssertException("Bitfield types are not diffable at this time"); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java index c3014dfbd4..45232d0c92 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java @@ -138,7 +138,7 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio // body buffy.append(BR); - buffy.append("TypeDef Base Data Type: ").append(BR).append(BR); + buffy.append("TypeDef Base Data Type: ").append(BR); iterator = bodyLines.iterator(); for (; iterator.hasNext();) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java index 942551e787..d79a4ef20c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java @@ -648,6 +648,7 @@ public class DataTypesXmlMgr { private void writerMember(XmlWriter writer, DataTypeComponent member) { XmlAttributes attrs = new XmlAttributes(); + // TODO: how should we output bitfields (aligned/unaligned) and flex array attrs.addAttribute("OFFSET", member.getOffset(), true); attrs.addAttribute("DATATYPE", member.getDataType().getDisplayName()); attrs.addAttribute("DATATYPE_NAMESPACE", member.getDataType().getCategoryPath().getPath()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java index 162d24caaf..a19a196853 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java @@ -935,6 +935,7 @@ public class ProgramDiffDetails { " (flexible array) " + ((comment != null) ? comment : "") + " " + newLine); } else { + // TODO: how should we display bitfields? buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " + "Ordinal=" + ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) + " " + @@ -1505,8 +1506,8 @@ public class ProgramDiffDetails { private boolean addSpecificCommentDetails(int commentType, String commentName) { boolean hasCommentDiff = false; try { - for (Address p1Address = minP1Address; p1Address - .compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1L)) { + for (Address p1Address = minP1Address; p1Address.compareTo( + maxP1Address) <= 0; p1Address = p1Address.add(1L)) { Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2); String noComment = "No " + commentName + "."; String cmt1 = l1.getComment(commentType, p1Address); @@ -2209,9 +2210,8 @@ public class ProgramDiffDetails { for (String propertyName : names1) { if (cu.hasProperty(propertyName)) { // Handle case where the class for a Saveable property is missing (unsupported). - if (cu.getProgram() - .getListing() - .getPropertyMap(propertyName) instanceof UnsupportedMapDB) { + if (cu.getProgram().getListing().getPropertyMap( + propertyName) instanceof UnsupportedMapDB) { buf.append( indent2 + propertyName + " is an unsupported property." + newLine); continue; @@ -2282,8 +2282,8 @@ public class ProgramDiffDetails { BookmarkManager bmm1 = p1.getBookmarkManager(); BookmarkManager bmm2 = p2.getBookmarkManager(); try { - for (Address p1Address = minP1Address; p1Address - .compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1)) { + for (Address p1Address = minP1Address; p1Address.compareTo( + maxP1Address) <= 0; p1Address = p1Address.add(1)) { Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2); Bookmark[] marks1 = bmm1.getBookmarks(p1Address); Arrays.sort(marks1, BOOKMARK_COMPARATOR); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java index dab59a5dd6..565c90ce7c 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java @@ -46,16 +46,16 @@ public class DataTypeDependencyOrderer { private DataTypeManager dtManager; // A HashSet is chosen so that we have no duplicates. - private HashSet inputSet = new HashSet(); + private HashSet inputSet = new HashSet<>(); - private HashSet procSet = new HashSet(); - private HashSet doneSet = new HashSet(); - private ArrayList structList = new ArrayList(); - private ArrayList orderedDependentsList = new ArrayList(); + private HashSet procSet = new HashSet<>(); + private HashSet doneSet = new HashSet<>(); + private ArrayList structList = new ArrayList<>(); + private ArrayList orderedDependentsList = new ArrayList<>(); - private HashMap> whoIDependOn = new HashMap>(); - private HashMap> whoDependsOnMe = new HashMap>(); - private LinkedList noDependentsQueue = new LinkedList(); + private HashMap> whoIDependOn = new HashMap<>(); + private HashMap> whoDependsOnMe = new HashMap<>(); + private LinkedList noDependentsQueue = new LinkedList<>(); /** * Associate a DataType with its ID (relative to the DataTypeManager) in an Entry @@ -152,7 +152,7 @@ public class DataTypeDependencyOrderer { if (processed == false) { processDependencyLists(); } - return new Pair, ArrayList>(structList, orderedDependentsList); + return new Pair<>(structList, orderedDependentsList); } /** @@ -189,8 +189,8 @@ public class DataTypeDependencyOrderer { whoDependsOnMe.size() + "\n\n"); if (!orderedDependentsList.isEmpty()) { for (DataType dt : orderedDependentsList) { - res.append("Ordered Dependents: " + dt.getName() + " " + dt.getClass().getName() + - "\n"); + res.append( + "Ordered Dependents: " + dt.getName() + " " + dt.getClass().getName() + "\n"); } } res.append("\n"); @@ -260,10 +260,14 @@ public class DataTypeDependencyOrderer { addDependent(entry, ((TypeDef) dataType).getDataType()); } else if (dataType instanceof Structure) { - DataTypeComponent dtcomps[] = ((Structure) dataType).getComponents(); + Structure struct = (Structure) dataType; + DataTypeComponent dtcomps[] = struct.getComponents(); for (DataTypeComponent dtcomp : dtcomps) { addDependent(entry, dtcomp.getDataType()); } + if (struct.hasFlexibleArrayComponent()) { + addDependent(entry, struct.getFlexibleArrayComponent().getDataType()); + } } else if (dataType instanceof Composite) { DataTypeComponent dtcomps[] = ((Composite) dataType).getComponents(); @@ -331,6 +335,9 @@ public class DataTypeDependencyOrderer { if ((entry == null) || (subType == null)) { return; } + if (subType instanceof BitFieldDataType) { + subType = ((BitFieldDataType) subType).getBaseDataType(); + } Entry subEntry = createEntry(subType); if (!doneSet.contains(subEntry)) { procSet.add(subEntry); @@ -343,13 +350,13 @@ public class DataTypeDependencyOrderer { } Set dependents = whoDependsOnMe.get(subEntry); if (dependents == null) { - dependents = new HashSet(); + dependents = new HashSet<>(); whoDependsOnMe.put(subEntry, dependents); } dependents.add(entry); //ignores duplicates Set support = whoIDependOn.get(entry); if (support == null) { - support = new HashSet(); + support = new HashSet<>(); whoIDependOn.put(entry, support); } support.add(subEntry); //ignores duplicates @@ -361,10 +368,10 @@ public class DataTypeDependencyOrderer { } Set dependents = whoDependsOnMe.get(entry); if (dependents == null) { - dependents = new HashSet(); + dependents = new HashSet<>(); whoDependsOnMe.put(entry, dependents); } - Set support = new HashSet(); + Set support = new HashSet<>(); whoIDependOn.put(entry, support); } 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 29614610a2..c176346152 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 @@ -2151,7 +2151,7 @@ public class CodeManager implements ErrorHandler, ManagerDB { } private void createReference(Data data, Address toAddr, List
longSegmentAddressList) { - if (toAddr == null) { + if (toAddr == null || !toAddr.isLoadedMemoryAddress()) { return; } 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 7d28efc84e..1e8006a3a1 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 @@ -421,6 +421,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { for (DataTypeComponent comp : comps) { comp.getDataType().addParent(dt); } + if (dt instanceof Structure) { + Structure struct = (Structure) dt; + if (struct.hasFlexibleArrayComponent()) { + struct.getFlexibleArrayComponent().getDataType().addParent(dt); + } + } } else if (dt instanceof FunctionDefinition) { FunctionDefinition funDef = (FunctionDefinition) dt; 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 9939f0952e..efb1da1e02 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 @@ -649,6 +649,7 @@ class StructureDB extends CompositeDB implements Structure { return 1; // Unaligned } if (alignment <= 0) { + // just in case - alignment should have been previously determined and stored StructurePackResult packResult = AlignedStructureInspector.packComponents(this); alignment = packResult.alignment; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java index fd1b776826..9b9a11f04b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java @@ -23,6 +23,11 @@ import javax.help.UnsupportedOperationException; import ghidra.docking.settings.Settings; import ghidra.util.exception.DuplicateNameException; +/** + * AlignedStructureInspector provides a simple instance of a structure + * member container used to perform alignment operations without forcing modification + * of the actual structure. + */ public class AlignedStructureInspector extends AlignedStructurePacker { private AlignedStructureInspector(Structure structure) { @@ -34,6 +39,9 @@ public class AlignedStructureInspector extends AlignedStructurePacker { for (DataTypeComponent c : structure.getComponents()) { list.add(new ReadOnlyComponentWrapper(c)); } + if (structure.hasFlexibleArrayComponent()) { + list.add(new ReadOnlyComponentWrapper(structure.getFlexibleArrayComponent())); + } return list; } @@ -55,10 +63,10 @@ public class AlignedStructureInspector extends AlignedStructurePacker { } @Override - public void update(int ordinal, int offset, int length) { - this.ordinal = ordinal; - this.offset = offset; - this.length = length; + public void update(int ord, int off, int len) { + this.ordinal = ord; + this.offset = off; + this.length = len; } @Override @@ -73,7 +81,7 @@ public class AlignedStructureInspector extends AlignedStructurePacker { @Override public boolean isFlexibleArrayComponent() { - return false; + return component.isFlexibleArrayComponent(); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java index cab42075a3..12ced56f50 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java @@ -340,7 +340,18 @@ public class BitFieldDataType extends AbstractDataType { @Override public String getDescription() { - return getName() + " BitField"; + StringBuffer sbuf = new StringBuffer(); + sbuf.append(Integer.toString(effectiveBitSize)); + sbuf.append("-bit "); + DataType dt = getBaseDataType(); + sbuf.append(dt.getDisplayName()); + sbuf.append(" bitfield"); + if (effectiveBitSize != bitSize) { + sbuf.append(" (declared as "); + sbuf.append(Integer.toString(bitSize)); + sbuf.append("-bits)"); + } + return sbuf.toString(); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java index 63cbc66427..92279f5ad6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java @@ -242,7 +242,7 @@ public class DataTypeWriter { } Msg.error(this, "Factory data types may not be written - type: " + dt, iae); } - if (dt instanceof Pointer || dt instanceof Array) { + if (dt instanceof Pointer || dt instanceof Array || dt instanceof BitFieldDataType) { write(getBaseDataType(dt), monitor); return; } @@ -308,6 +308,9 @@ public class DataTypeWriter { else if (dt instanceof BuiltInDataType) { writeBuiltIn((BuiltInDataType) dt, monitor); } + else if (dt instanceof BitFieldDataType) { + // skip + } else { writer.write(EOL); writer.write(EOL); @@ -540,7 +543,12 @@ public class DataTypeWriter { if (componentString == null) { - if (dataType instanceof Array) { + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bfDt = (BitFieldDataType) dataType; + name += ":" + bfDt.getDeclaredBitSize(); + dataType = bfDt.getBaseDataType(); + } + else if (dataType instanceof Array) { Array array = (Array) dataType; name += getArrayDimensions(array); dataType = getArrayBaseType(array); @@ -637,6 +645,7 @@ public class DataTypeWriter { return; } } + // TODO: A comment explaining the special 'P' case would be helpful!! Smells like fish. else if (baseType instanceof Pointer && typedefName.startsWith("P")) { DataType dt = ((Pointer) baseType).getDataType(); if (dt instanceof TypeDef) { @@ -765,6 +774,10 @@ public class DataTypeWriter { Pointer pointer = (Pointer) dt; dt = pointer.getDataType(); } + else if (dt instanceof BitFieldDataType) { + BitFieldDataType bitfieldDt = (BitFieldDataType) dt; + dt = bitfieldDt.getBaseDataType(); + } else { break; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java index f21897737b..32175bd83d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java @@ -25,9 +25,9 @@ public interface InternalDataTypeComponent extends DataTypeComponent { /** * Update component ordinal, offset and length during alignment - * @param ordinal - * @param offset - * @param length + * @param ordinal updated ordinal + * @param offset updated offset + * @param length updated byte length */ void update(int ordinal, int offset, int length);