Bitfields - added simple bitfield viewer and corrected missing support

for bitfields and flex arrays
This commit is contained in:
ghidra1 2019-06-05 18:26:57 -04:00
parent 52f6bfc127
commit 31163bca26
26 changed files with 747 additions and 233 deletions

View file

@ -23,6 +23,7 @@ import javax.swing.SwingUtilities;
import docking.ActionContext; import docking.ActionContext;
import docking.DockingWindowManager; import docking.DockingWindowManager;
import ghidra.program.model.data.Structure;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
/** /**
@ -76,7 +77,8 @@ public class AddBitFieldAction extends CompositeEditorTableAction {
public void adjustEnablement() { public void adjustEnablement() {
boolean enabled = true; boolean enabled = true;
CompEditorModel editorModel = (CompEditorModel) model; 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()) { editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) {
enabled = false; enabled = false;
} }

View file

@ -20,15 +20,12 @@ import java.awt.event.MouseEvent;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import docking.*; import docking.ActionContext;
import docking.DialogComponentProvider;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.GhidraApplicationLayout;
import ghidra.app.plugin.core.analysis.DefaultDataTypeManagerService;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.SystemUtilities;
import resources.ResourceManager; import resources.ResourceManager;
public class BitFieldEditorDialog extends DialogComponentProvider { public class BitFieldEditorDialog extends DialogComponentProvider {
@ -93,7 +90,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
if (bitfieldDtc == null || !bitFieldEditorPanel.endCurrentEdit()) { if (bitfieldDtc == null || !bitFieldEditorPanel.endCurrentEdit()) {
return; return;
} }
initEdit(bitfieldDtc.getOrdinal()); initEdit(bitfieldDtc.getOrdinal(), true);
} }
@Override @Override
@ -200,7 +197,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
initAdd(-editOrdinal - 1); initAdd(-editOrdinal - 1);
} }
else { else {
initEdit(editOrdinal); initEdit(editOrdinal, false);
} }
return bitFieldEditorPanel; return bitFieldEditorPanel;
} }
@ -234,16 +231,19 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
setApplyEnabled(true); setApplyEnabled(true);
} }
private void initEdit(int editOrdinal) throws ArrayIndexOutOfBoundsException { private void initEdit(int editOrdinal, boolean useExistingAllocationSize)
throws ArrayIndexOutOfBoundsException {
DataTypeComponent dtc = composite.getComponent(editOrdinal); DataTypeComponent dtc = composite.getComponent(editOrdinal);
if (!dtc.isBitFieldComponent()) { if (!dtc.isBitFieldComponent()) {
throw new IllegalArgumentException("editOrdinal does not correspond to bitfield"); throw new IllegalArgumentException("editOrdinal does not correspond to bitfield");
} }
bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc)); bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc),
useExistingAllocationSize);
setApplyEnabled(true); setApplyEnabled(true);
} }
private int getPreferredAllocationOffset(DataTypeComponent bitfieldDtc) { static int getPreferredAllocationOffset(DataTypeComponent bitfieldDtc) {
Composite composite = (Composite) bitfieldDtc.getParent();
if (composite instanceof Union) { if (composite instanceof Union) {
return 0; return 0;
} }
@ -270,34 +270,4 @@ public class BitFieldEditorDialog extends DialogComponentProvider {
return offset; 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);
});
}
} }

View file

@ -39,8 +39,8 @@ import ghidra.util.layout.*;
import resources.ResourceManager; import resources.ResourceManager;
/** /**
* <code>BitFieldEditorPanel</code> provides the ability to place bitfields * <code>BitFieldEditorPanel</code> provides the ability to add or modify bitfields
* within unaligned structures and unions. * within unaligned structures.
*/ */
public class BitFieldEditorPanel extends JPanel { public class BitFieldEditorPanel extends JPanel {
@ -100,7 +100,7 @@ public class BitFieldEditorPanel extends JPanel {
private JPanel createLegendPanel() { private JPanel createLegendPanel() {
JPanel legendPanel = new JPanel(new BorderLayout()); JPanel legendPanel = new JPanel(new BorderLayout());
legendPanel.add(new BitFieldPlacementComponent.BitFieldLegend(), BorderLayout.WEST); legendPanel.add(new BitFieldPlacementComponent.BitFieldLegend(null), BorderLayout.WEST);
return legendPanel; return legendPanel;
} }
@ -390,15 +390,19 @@ public class BitFieldEditorPanel extends JPanel {
* If null an allocation size of 4-bytes will be used but may be adjusted. * If null an allocation size of 4-bytes will be used but may be adjusted.
* @param bitfieldDtc bitfield component or null * @param bitfieldDtc bitfield component or null
* @param allocationOffset allocation offset to be used * @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; String initialFieldName = null;
DataType initialBaseDataType = null; DataType initialBaseDataType = null;
int allocationSize = -1; int allocationSize = -1;
if (useExistingAllocationSize) {
BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation();
if (bitFieldAllocation != null) { if (bitFieldAllocation != null) {
allocationSize = bitFieldAllocation.getAllocationByteSize(); allocationSize = bitFieldAllocation.getAllocationByteSize();
} }
}
if (bitfieldDtc != null) { if (bitfieldDtc != null) {
if (!bitfieldDtc.isBitFieldComponent()) { if (!bitfieldDtc.isBitFieldComponent()) {
throw new IllegalArgumentException("unsupport data type component"); throw new IllegalArgumentException("unsupport data type component");
@ -420,7 +424,7 @@ public class BitFieldEditorPanel extends JPanel {
// TODO: adjust offset and allocationSize if needed // TODO: adjust offset and allocationSize if needed
placementComponent.setAllocationOffset(allocationOffset); placementComponent.setAllocationOffset(allocationOffset);
placementComponent.init(allocationSize, bitfieldDtc); placementComponent.init(allocationSize, bitfieldDtc);
bitFieldAllocation = placementComponent.getBitFieldAllocation(); // get updated instance BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation(); // get updated instance
initControls(initialFieldName, initialBaseDataType, bitFieldAllocation.getBitSize()); initControls(initialFieldName, initialBaseDataType, bitFieldAllocation.getBitSize());
enableControls(bitfieldDtc != null); enableControls(bitfieldDtc != null);
} }

View file

@ -26,6 +26,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite; import ghidra.program.model.data.Composite;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.layout.VerticalLayout;
import resources.icons.ColorIcon; import resources.icons.ColorIcon;
public class BitFieldPlacementComponent extends JPanel { public class BitFieldPlacementComponent extends JPanel {
@ -59,28 +60,51 @@ public class BitFieldPlacementComponent extends JPanel {
private EditMode editMode = EditMode.NONE; private EditMode editMode = EditMode.NONE;
private int editOrdinal = -1; // FIXME: improve insert use private int editOrdinal = -1; // FIXME: improve insert use
private DataTypeComponent editComponent;
public static class BitFieldLegend extends JPanel { public static class BitFieldLegend extends JPanel {
BitFieldLegend() { BitFieldLegend(DataTypeComponent viewedBitfield) {
setLayout(new GridLayout(2, 3, 5, 5)); JPanel legendPanel;
//setLayout(new RowColumnLayout(10, 10, RowColumnLayout.ROW, 0)); if (viewedBitfield != null) {
add(new JLabel("Undefined bits", setLayout(new VerticalLayout(10));
new ColorIcon(UNDEFINED_BIT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), legendPanel = new JPanel(new GridLayout(1, 3, 5, 5));
SwingConstants.LEFT)); String viewComponentText =
add(new JLabel("Defined bitfield", "Selected bitfield { " + viewedBitfield.getDataType().getDisplayName();
new ColorIcon(BITFIELD_COMPONENT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), String viewComponentName = viewedBitfield.getFieldName();
SwingConstants.LEFT)); if (viewComponentName != null) {
add(new JLabel("Defined non-bitfield", viewComponentText += " " + viewComponentName;
new ColorIcon(NON_BITFIELD_COMPONENT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), }
SwingConstants.LEFT)); viewComponentText += " }";
add(new JLabel("Edit bitfield bits", add(new JLabel(viewComponentText,
new ColorIcon(BITFIELD_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE), new ColorIcon(BITFIELD_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE),
SwingConstants.LEFT)); SwingConstants.LEFT));
add(new JLabel("Conflict bits", 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));
legendPanel.add(new JLabel("Defined non-bitfield ",
new ColorIcon(NON_BITFIELD_COMPONENT_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE),
SwingConstants.LEFT));
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), new ColorIcon(CONFLICT_BITS_COLOR, INTERIOR_LINE_COLOR, LENEND_BOX_SIZE),
SwingConstants.LEFT)); SwingConstants.LEFT));
} }
}
} }
@ -127,7 +151,8 @@ public class BitFieldPlacementComponent extends JPanel {
} }
void refresh(int allocationByteSize, int bitSize, int bitOffset) { void refresh(int allocationByteSize, int bitSize, int bitOffset) {
bitFieldAllocation = new BitFieldAllocation(allocationByteSize, bitSize, bitOffset); bitFieldAllocation =
new BitFieldAllocation(allocationByteSize, bitSize, bitOffset, editComponent);
updatePreferredSize(); updatePreferredSize();
repaint(); repaint();
} }
@ -147,14 +172,16 @@ public class BitFieldPlacementComponent extends JPanel {
void initAdd(int allocationByteSize, int bitSize, int bitOffset) { void initAdd(int allocationByteSize, int bitSize, int bitOffset) {
editMode = EditMode.ADD; editMode = EditMode.ADD;
editOrdinal = -1; editOrdinal = -1;
editComponent = null;
refresh(allocationByteSize, bitSize, bitOffset); 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; editMode = EditMode.NONE;
editOrdinal = -1; editOrdinal = -1;
this.editComponent = null;
refresh(allocationByteSize, 0, 0); refresh(allocationByteSize, 0, 0);
return; return;
} }
@ -163,12 +190,13 @@ public class BitFieldPlacementComponent extends JPanel {
// of the component being modified // of the component being modified
editMode = EditMode.EDIT; 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 = bitFieldAllocation =
new BitFieldAllocation(allocationByteSize, placement.rightBit - placement.leftBit + 1, new BitFieldAllocation(allocationByteSize, placement.rightBit - placement.leftBit + 1,
(8 * allocationByteSize) - placement.rightBit - 1); (8 * allocationByteSize) - placement.rightBit - 1, editDtc);
updatePreferredSize(); updatePreferredSize();
repaint(); repaint();
} }
@ -213,6 +241,7 @@ public class BitFieldPlacementComponent extends JPanel {
// unexpected removal // unexpected removal
editMode = EditMode.ADD; editMode = EditMode.ADD;
editOrdinal = -1; editOrdinal = -1;
editComponent = null;
} }
else if (ordinal < editOrdinal) { else if (ordinal < editOrdinal) {
--editOrdinal; --editOrdinal;
@ -268,6 +297,7 @@ public class BitFieldPlacementComponent extends JPanel {
finally { finally {
editMode = EditMode.NONE; editMode = EditMode.NONE;
editOrdinal = -1; editOrdinal = -1;
editComponent = null;
bitFieldAllocation.refresh(); bitFieldAllocation.refresh();
repaint(); repaint();
} }
@ -475,18 +505,21 @@ public class BitFieldPlacementComponent extends JPanel {
private final int bitSize; private final int bitSize;
private final int bitOffset; private final int bitOffset;
private boolean hasConflict; private boolean hasConflict;
private DataTypeComponent editComponent;
// bit layout normalized to big-endian layout // bit layout normalized to big-endian layout
// left-most allocation msb has array index of 0 // left-most allocation msb has array index of 0
private BitAttributes[] bitAttributes; 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)) { if (allocationByteSize <= 0 || (bitSize + bitOffset) > (8 * allocationByteSize)) {
throw new IllegalArgumentException("allocation size too small"); throw new IllegalArgumentException("allocation size too small");
} }
this.allocationByteSize = allocationByteSize; this.allocationByteSize = allocationByteSize;
this.bitSize = bitSize; this.bitSize = bitSize;
this.bitOffset = bitOffset; this.bitOffset = bitOffset;
this.editComponent = editComponent;
refresh(); refresh();
} }
@ -505,11 +538,11 @@ public class BitFieldPlacementComponent extends JPanel {
if (editMode != EditMode.NONE) { if (editMode != EditMode.NONE) {
int rightMostBit = bitAttributes.length - bitOffset - 1; int rightMostBit = bitAttributes.length - bitOffset - 1;
if (bitSize == 0) { if (bitSize == 0) {
allocateZeroBitField(null, rightMostBit); allocateZeroBitField(editComponent, rightMostBit);
} }
else { else {
int leftMostBit = rightMostBit - bitSize + 1; 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) { if (zeroBitfield) {
return UNDEFINED_BIT_COLOR; return UNDEFINED_BIT_COLOR;
} }
if (dtc == null) { if (dtc == editComponent) {
return BITFIELD_BITS_COLOR; // edit field return BITFIELD_BITS_COLOR; // edit field
} }
return dtc.isBitFieldComponent() ? BITFIELD_COMPONENT_COLOR return dtc.isBitFieldComponent() ? BITFIELD_COMPONENT_COLOR

View file

@ -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;
}
}

View file

@ -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.*;
/**
* <code>BitFieldViewerPanel</code> 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();
}
}

View file

@ -1231,4 +1231,11 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen
throw new DuplicateNameException("Data type named " + name + " already exists"); 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);
}
} }

View file

@ -45,10 +45,7 @@ import docking.widgets.label.GLabel;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import docking.widgets.table.GTableCellRenderer; import docking.widgets.table.GTableCellRenderer;
import docking.widgets.textfield.GValidatedTextField; 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.services.DataTypeManagerService;
import ghidra.app.util.ToolTipUtils;
import ghidra.app.util.datatype.DataTypeSelectionEditor; import ghidra.app.util.datatype.DataTypeSelectionEditor;
import ghidra.app.util.datatype.NavigationDirection; import ghidra.app.util.datatype.NavigationDirection;
import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.Plugin;
@ -144,7 +141,8 @@ public abstract class CompositeEditorPanel extends JPanel
private void setupTableCellRenderer() { private void setupTableCellRenderer() {
GTableCellRenderer cellRenderer = new GTableCellRenderer(); GTableCellRenderer cellRenderer = new GTableCellRenderer();
DataTypeCellRenderer dtiCellRenderer = new DataTypeCellRenderer(); DataTypeCellRenderer dtiCellRenderer = new DataTypeCellRenderer(
model.getOriginalDataTypeManager(), model.bitfieldsSupported());
table.setDefaultRenderer(String.class, cellRenderer); table.setDefaultRenderer(String.class, cellRenderer);
table.setDefaultRenderer(DataTypeInstance.class, dtiCellRenderer); table.setDefaultRenderer(DataTypeInstance.class, dtiCellRenderer);
} }
@ -558,6 +556,12 @@ public abstract class CompositeEditorPanel extends JPanel
table = new CompositeTable(model); table = new CompositeTable(model);
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
table.addMouseListener(new CompositeTableMouseListener()); table.addMouseListener(new CompositeTableMouseListener());
if (model.bitfieldsSupported()) {
CompositeTableCellMouseListener cellMouseListener =
new CompositeTableCellMouseListener();
table.addMouseListener(cellMouseListener);
table.addMouseMotionListener(cellMouseListener);
}
CompositeEditorTableAction action = provider.actionMgr.getNamedAction( CompositeEditorTableAction action = provider.actionMgr.getNamedAction(
CompositeEditorTableAction.EDIT_ACTION_PREFIX + EditFieldAction.ACTION_NAME); 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 { private class CompositeTableMouseListener extends MouseAdapter {
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
@ -1473,69 +1556,6 @@ public abstract class CompositeEditorPanel extends JPanel
super(dm); 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 = "<HTML><b>" + displayName + "</b><BR>";
toolTipText = toolTipText.replace("<HTML>", headerText);
return toolTipText;
}
@Override @Override
// overridden because the editor component was not being given focus // overridden because the editor component was not being given focus
public Component prepareEditor(TableCellEditor editor, int row, int column) { public Component prepareEditor(TableCellEditor editor, int row, int column) {

View file

@ -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 = "<HTML><b>" + displayName + "</b><BR>";
toolTipText = toolTipText.replace("<HTML>", headerText);
return toolTipText;
}
}

View file

@ -71,7 +71,8 @@ public class StructureEditorProvider extends CompositeEditorProvider {
new HexNumbersAction(this), new HexNumbersAction(this),
new CreateInternalStructureAction(this), new CreateInternalStructureAction(this),
new AddBitFieldAction(this), new AddBitFieldAction(this),
new EditBitFieldAction(this) new EditBitFieldAction(this),
// new ViewBitFieldAction(this)
}; };
//@formatter:on //@formatter:on
} }

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -95,6 +95,9 @@ public class FindReferencesToFieldAction extends DockingAction {
DataTypeComponent[] components = composite.getComponents(); DataTypeComponent[] components = composite.getComponents();
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
for (DataTypeComponent dataTypeComponent : components) { for (DataTypeComponent dataTypeComponent : components) {
if (dataTypeComponent.isBitFieldComponent()) {
continue;
}
String fieldName = dataTypeComponent.getFieldName(); String fieldName = dataTypeComponent.getFieldName();
if (StringUtils.isBlank(fieldName)) { if (StringUtils.isBlank(fieldName)) {
continue; continue;

View file

@ -92,6 +92,9 @@ public class ToolTipUtils {
else if (dataType instanceof Array) { else if (dataType instanceof Array) {
return new ArrayDataTypeHTMLRepresentation((Array) dataType); return new ArrayDataTypeHTMLRepresentation((Array) dataType);
} }
else if (dataType instanceof BitFieldDataType) {
return new BitFieldDataTypeHTMLRepresentation((BitFieldDataType) dataType);
}
else { else {
return new DefaultDataTypeHTMLRepresentation(dataType); return new DefaultDataTypeHTMLRepresentation(dataType);
} }
@ -203,8 +206,9 @@ public class ToolTipUtils {
buf.append("<td width=\"1%\">"); buf.append("<td width=\"1%\">");
buf.append(colorString(Color.BLACK, friendlyEncodeHTML(param.getDataType().getName()))); buf.append(colorString(Color.BLACK, friendlyEncodeHTML(param.getDataType().getName())));
buf.append("</td><td width=\"1%\">"); buf.append("</td><td width=\"1%\">");
Color paramColor = param.getFunction().hasCustomVariableStorage() Color paramColor =
? PARAM_CUSTOM_STORAGE_COLOR : PARAM_DYNAMIC_STORAGE_COLOR; param.getFunction().hasCustomVariableStorage() ? PARAM_CUSTOM_STORAGE_COLOR
: PARAM_DYNAMIC_STORAGE_COLOR;
buf.append( buf.append(
colorString(paramColor, friendlyEncodeHTML(param.getVariableStorage().toString()))); colorString(paramColor, friendlyEncodeHTML(param.getVariableStorage().toString())));
buf.append("</td><td width=\"1%\">"); buf.append("</td><td width=\"1%\">");

View file

@ -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");
}
}

View file

@ -138,7 +138,7 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
// body // body
buffy.append(BR); buffy.append(BR);
buffy.append("TypeDef Base Data Type: ").append(BR).append(BR); buffy.append("TypeDef Base Data Type: ").append(BR);
iterator = bodyLines.iterator(); iterator = bodyLines.iterator();
for (; iterator.hasNext();) { for (; iterator.hasNext();) {

View file

@ -648,6 +648,7 @@ public class DataTypesXmlMgr {
private void writerMember(XmlWriter writer, DataTypeComponent member) { private void writerMember(XmlWriter writer, DataTypeComponent member) {
XmlAttributes attrs = new XmlAttributes(); XmlAttributes attrs = new XmlAttributes();
// TODO: how should we output bitfields (aligned/unaligned) and flex array
attrs.addAttribute("OFFSET", member.getOffset(), true); attrs.addAttribute("OFFSET", member.getOffset(), true);
attrs.addAttribute("DATATYPE", member.getDataType().getDisplayName()); attrs.addAttribute("DATATYPE", member.getDataType().getDisplayName());
attrs.addAttribute("DATATYPE_NAMESPACE", member.getDataType().getCategoryPath().getPath()); attrs.addAttribute("DATATYPE_NAMESPACE", member.getDataType().getCategoryPath().getPath());

View file

@ -935,6 +935,7 @@ public class ProgramDiffDetails {
" (flexible array) " + ((comment != null) ? comment : "") + " " + newLine); " (flexible array) " + ((comment != null) ? comment : "") + " " + newLine);
} }
else { else {
// TODO: how should we display bitfields?
buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " + buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " +
"Ordinal=" + ordinal + " " + fieldName + " " + "Ordinal=" + ordinal + " " + fieldName + " " +
actualDt.getMnemonic(actualDt.getDefaultSettings()) + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) + " " +
@ -1505,8 +1506,8 @@ public class ProgramDiffDetails {
private boolean addSpecificCommentDetails(int commentType, String commentName) { private boolean addSpecificCommentDetails(int commentType, String commentName) {
boolean hasCommentDiff = false; boolean hasCommentDiff = false;
try { try {
for (Address p1Address = minP1Address; p1Address for (Address p1Address = minP1Address; p1Address.compareTo(
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1L)) { maxP1Address) <= 0; p1Address = p1Address.add(1L)) {
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2); Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
String noComment = "No " + commentName + "."; String noComment = "No " + commentName + ".";
String cmt1 = l1.getComment(commentType, p1Address); String cmt1 = l1.getComment(commentType, p1Address);
@ -2209,9 +2210,8 @@ public class ProgramDiffDetails {
for (String propertyName : names1) { for (String propertyName : names1) {
if (cu.hasProperty(propertyName)) { if (cu.hasProperty(propertyName)) {
// Handle case where the class for a Saveable property is missing (unsupported). // Handle case where the class for a Saveable property is missing (unsupported).
if (cu.getProgram() if (cu.getProgram().getListing().getPropertyMap(
.getListing() propertyName) instanceof UnsupportedMapDB) {
.getPropertyMap(propertyName) instanceof UnsupportedMapDB) {
buf.append( buf.append(
indent2 + propertyName + " is an unsupported property." + newLine); indent2 + propertyName + " is an unsupported property." + newLine);
continue; continue;
@ -2282,8 +2282,8 @@ public class ProgramDiffDetails {
BookmarkManager bmm1 = p1.getBookmarkManager(); BookmarkManager bmm1 = p1.getBookmarkManager();
BookmarkManager bmm2 = p2.getBookmarkManager(); BookmarkManager bmm2 = p2.getBookmarkManager();
try { try {
for (Address p1Address = minP1Address; p1Address for (Address p1Address = minP1Address; p1Address.compareTo(
.compareTo(maxP1Address) <= 0; p1Address = p1Address.add(1)) { maxP1Address) <= 0; p1Address = p1Address.add(1)) {
Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2); Address p2Address = SimpleDiffUtility.getCompatibleAddress(p1, p1Address, p2);
Bookmark[] marks1 = bmm1.getBookmarks(p1Address); Bookmark[] marks1 = bmm1.getBookmarks(p1Address);
Arrays.sort(marks1, BOOKMARK_COMPARATOR); Arrays.sort(marks1, BOOKMARK_COMPARATOR);

View file

@ -46,16 +46,16 @@ public class DataTypeDependencyOrderer {
private DataTypeManager dtManager; private DataTypeManager dtManager;
// A HashSet is chosen so that we have no duplicates. // A HashSet is chosen so that we have no duplicates.
private HashSet<Entry> inputSet = new HashSet<Entry>(); private HashSet<Entry> inputSet = new HashSet<>();
private HashSet<Entry> procSet = new HashSet<Entry>(); private HashSet<Entry> procSet = new HashSet<>();
private HashSet<Entry> doneSet = new HashSet<Entry>(); private HashSet<Entry> doneSet = new HashSet<>();
private ArrayList<DataType> structList = new ArrayList<DataType>(); private ArrayList<DataType> structList = new ArrayList<>();
private ArrayList<DataType> orderedDependentsList = new ArrayList<DataType>(); private ArrayList<DataType> orderedDependentsList = new ArrayList<>();
private HashMap<Entry, Set<Entry>> whoIDependOn = new HashMap<Entry, Set<Entry>>(); private HashMap<Entry, Set<Entry>> whoIDependOn = new HashMap<>();
private HashMap<Entry, Set<Entry>> whoDependsOnMe = new HashMap<Entry, Set<Entry>>(); private HashMap<Entry, Set<Entry>> whoDependsOnMe = new HashMap<>();
private LinkedList<Entry> noDependentsQueue = new LinkedList<Entry>(); private LinkedList<Entry> noDependentsQueue = new LinkedList<>();
/** /**
* Associate a DataType with its ID (relative to the DataTypeManager) in an Entry * Associate a DataType with its ID (relative to the DataTypeManager) in an Entry
@ -152,7 +152,7 @@ public class DataTypeDependencyOrderer {
if (processed == false) { if (processed == false) {
processDependencyLists(); processDependencyLists();
} }
return new Pair<ArrayList<DataType>, ArrayList<DataType>>(structList, orderedDependentsList); return new Pair<>(structList, orderedDependentsList);
} }
/** /**
@ -189,8 +189,8 @@ public class DataTypeDependencyOrderer {
whoDependsOnMe.size() + "\n\n"); whoDependsOnMe.size() + "\n\n");
if (!orderedDependentsList.isEmpty()) { if (!orderedDependentsList.isEmpty()) {
for (DataType dt : orderedDependentsList) { for (DataType dt : orderedDependentsList) {
res.append("Ordered Dependents: " + dt.getName() + " " + dt.getClass().getName() + res.append(
"\n"); "Ordered Dependents: " + dt.getName() + " " + dt.getClass().getName() + "\n");
} }
} }
res.append("\n"); res.append("\n");
@ -260,10 +260,14 @@ public class DataTypeDependencyOrderer {
addDependent(entry, ((TypeDef) dataType).getDataType()); addDependent(entry, ((TypeDef) dataType).getDataType());
} }
else if (dataType instanceof Structure) { else if (dataType instanceof Structure) {
DataTypeComponent dtcomps[] = ((Structure) dataType).getComponents(); Structure struct = (Structure) dataType;
DataTypeComponent dtcomps[] = struct.getComponents();
for (DataTypeComponent dtcomp : dtcomps) { for (DataTypeComponent dtcomp : dtcomps) {
addDependent(entry, dtcomp.getDataType()); addDependent(entry, dtcomp.getDataType());
} }
if (struct.hasFlexibleArrayComponent()) {
addDependent(entry, struct.getFlexibleArrayComponent().getDataType());
}
} }
else if (dataType instanceof Composite) { else if (dataType instanceof Composite) {
DataTypeComponent dtcomps[] = ((Composite) dataType).getComponents(); DataTypeComponent dtcomps[] = ((Composite) dataType).getComponents();
@ -331,6 +335,9 @@ public class DataTypeDependencyOrderer {
if ((entry == null) || (subType == null)) { if ((entry == null) || (subType == null)) {
return; return;
} }
if (subType instanceof BitFieldDataType) {
subType = ((BitFieldDataType) subType).getBaseDataType();
}
Entry subEntry = createEntry(subType); Entry subEntry = createEntry(subType);
if (!doneSet.contains(subEntry)) { if (!doneSet.contains(subEntry)) {
procSet.add(subEntry); procSet.add(subEntry);
@ -343,13 +350,13 @@ public class DataTypeDependencyOrderer {
} }
Set<Entry> dependents = whoDependsOnMe.get(subEntry); Set<Entry> dependents = whoDependsOnMe.get(subEntry);
if (dependents == null) { if (dependents == null) {
dependents = new HashSet<Entry>(); dependents = new HashSet<>();
whoDependsOnMe.put(subEntry, dependents); whoDependsOnMe.put(subEntry, dependents);
} }
dependents.add(entry); //ignores duplicates dependents.add(entry); //ignores duplicates
Set<Entry> support = whoIDependOn.get(entry); Set<Entry> support = whoIDependOn.get(entry);
if (support == null) { if (support == null) {
support = new HashSet<Entry>(); support = new HashSet<>();
whoIDependOn.put(entry, support); whoIDependOn.put(entry, support);
} }
support.add(subEntry); //ignores duplicates support.add(subEntry); //ignores duplicates
@ -361,10 +368,10 @@ public class DataTypeDependencyOrderer {
} }
Set<Entry> dependents = whoDependsOnMe.get(entry); Set<Entry> dependents = whoDependsOnMe.get(entry);
if (dependents == null) { if (dependents == null) {
dependents = new HashSet<Entry>(); dependents = new HashSet<>();
whoDependsOnMe.put(entry, dependents); whoDependsOnMe.put(entry, dependents);
} }
Set<Entry> support = new HashSet<Entry>(); Set<Entry> support = new HashSet<>();
whoIDependOn.put(entry, support); whoIDependOn.put(entry, support);
} }

View file

@ -2151,7 +2151,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
} }
private void createReference(Data data, Address toAddr, List<Address> longSegmentAddressList) { private void createReference(Data data, Address toAddr, List<Address> longSegmentAddressList) {
if (toAddr == null) { if (toAddr == null || !toAddr.isLoadedMemoryAddress()) {
return; return;
} }

View file

@ -421,6 +421,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
for (DataTypeComponent comp : comps) { for (DataTypeComponent comp : comps) {
comp.getDataType().addParent(dt); 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) { else if (dt instanceof FunctionDefinition) {
FunctionDefinition funDef = (FunctionDefinition) dt; FunctionDefinition funDef = (FunctionDefinition) dt;

View file

@ -649,6 +649,7 @@ class StructureDB extends CompositeDB implements Structure {
return 1; // Unaligned return 1; // Unaligned
} }
if (alignment <= 0) { if (alignment <= 0) {
// just in case - alignment should have been previously determined and stored
StructurePackResult packResult = AlignedStructureInspector.packComponents(this); StructurePackResult packResult = AlignedStructureInspector.packComponents(this);
alignment = packResult.alignment; alignment = packResult.alignment;
} }

View file

@ -23,6 +23,11 @@ import javax.help.UnsupportedOperationException;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
/**
* <code>AlignedStructureInspector</code> 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 { public class AlignedStructureInspector extends AlignedStructurePacker {
private AlignedStructureInspector(Structure structure) { private AlignedStructureInspector(Structure structure) {
@ -34,6 +39,9 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
for (DataTypeComponent c : structure.getComponents()) { for (DataTypeComponent c : structure.getComponents()) {
list.add(new ReadOnlyComponentWrapper(c)); list.add(new ReadOnlyComponentWrapper(c));
} }
if (structure.hasFlexibleArrayComponent()) {
list.add(new ReadOnlyComponentWrapper(structure.getFlexibleArrayComponent()));
}
return list; return list;
} }
@ -55,10 +63,10 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
} }
@Override @Override
public void update(int ordinal, int offset, int length) { public void update(int ord, int off, int len) {
this.ordinal = ordinal; this.ordinal = ord;
this.offset = offset; this.offset = off;
this.length = length; this.length = len;
} }
@Override @Override
@ -73,7 +81,7 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
@Override @Override
public boolean isFlexibleArrayComponent() { public boolean isFlexibleArrayComponent() {
return false; return component.isFlexibleArrayComponent();
} }
@Override @Override

View file

@ -340,7 +340,18 @@ public class BitFieldDataType extends AbstractDataType {
@Override @Override
public String getDescription() { 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 @Override

View file

@ -242,7 +242,7 @@ public class DataTypeWriter {
} }
Msg.error(this, "Factory data types may not be written - type: " + dt, iae); 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); write(getBaseDataType(dt), monitor);
return; return;
} }
@ -308,6 +308,9 @@ public class DataTypeWriter {
else if (dt instanceof BuiltInDataType) { else if (dt instanceof BuiltInDataType) {
writeBuiltIn((BuiltInDataType) dt, monitor); writeBuiltIn((BuiltInDataType) dt, monitor);
} }
else if (dt instanceof BitFieldDataType) {
// skip
}
else { else {
writer.write(EOL); writer.write(EOL);
writer.write(EOL); writer.write(EOL);
@ -540,7 +543,12 @@ public class DataTypeWriter {
if (componentString == null) { 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; Array array = (Array) dataType;
name += getArrayDimensions(array); name += getArrayDimensions(array);
dataType = getArrayBaseType(array); dataType = getArrayBaseType(array);
@ -637,6 +645,7 @@ public class DataTypeWriter {
return; return;
} }
} }
// TODO: A comment explaining the special 'P' case would be helpful!! Smells like fish.
else if (baseType instanceof Pointer && typedefName.startsWith("P")) { else if (baseType instanceof Pointer && typedefName.startsWith("P")) {
DataType dt = ((Pointer) baseType).getDataType(); DataType dt = ((Pointer) baseType).getDataType();
if (dt instanceof TypeDef) { if (dt instanceof TypeDef) {
@ -765,6 +774,10 @@ public class DataTypeWriter {
Pointer pointer = (Pointer) dt; Pointer pointer = (Pointer) dt;
dt = pointer.getDataType(); dt = pointer.getDataType();
} }
else if (dt instanceof BitFieldDataType) {
BitFieldDataType bitfieldDt = (BitFieldDataType) dt;
dt = bitfieldDt.getBaseDataType();
}
else { else {
break; break;
} }

View file

@ -25,9 +25,9 @@ public interface InternalDataTypeComponent extends DataTypeComponent {
/** /**
* Update component ordinal, offset and length during alignment * Update component ordinal, offset and length during alignment
* @param ordinal * @param ordinal updated ordinal
* @param offset * @param offset updated offset
* @param length * @param length updated byte length
*/ */
void update(int ordinal, int offset, int length); void update(int ordinal, int offset, int length);