BitFields - added preliminary support for composite bitfields

This commit is contained in:
ghidra1 2019-03-26 15:23:27 -04:00
parent c23ae691e2
commit a7345527c9
209 changed files with 18617 additions and 6720 deletions

View file

@ -921,11 +921,13 @@ src/main/resources/images/L.gif||GHIDRA||||END|
src/main/resources/images/LocalVariable.gif||GHIDRA||||END|
src/main/resources/images/M.gif||GHIDRA||||END|
src/main/resources/images/Merge.png||GHIDRA||||END|
src/main/resources/images/Minus.png||GHIDRA||||END|
src/main/resources/images/MultiDuplicateData.png||GHIDRA||||END|
src/main/resources/images/Namespace.gif||GHIDRA||||END|
src/main/resources/images/NextHighlightBlock16.gif||GHIDRA||||END|
src/main/resources/images/NextSelectionBlock16.gif||GHIDRA||||END|
src/main/resources/images/Parameter.gif||GHIDRA||||END|
src/main/resources/images/Plus.png||GHIDRA||||END|
src/main/resources/images/PreviousHighlightBlock16.gif||GHIDRA||||END|
src/main/resources/images/PreviousSelectionBlock16.gif||GHIDRA||||END|
src/main/resources/images/Program.gif||GHIDRA||||END|

View file

@ -163,6 +163,8 @@
be specified.<BR>
</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<H3>Unaligned Structures</H3>
@ -301,7 +303,9 @@
<IMG alt="" src="images/UnionEditorAligned.png">
</DIV>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="Structure_Editor_Flex_Array"></A>Flexible Array Component</H2>
<P>A structure may be defined with a trailing flexible array component which corresponds to
@ -321,7 +325,17 @@
be reflected in decompilation results. Its primary purpose if to reflect the C source definition
of a structure with correct alignment and structure sizing.</P>
<DIV style="text-align: center;">
<P><IMG alt="Note:" src="../../shared/note.png"> The equivalent of having <SPAN style=
"font-style: italic;">no C #pragma pack attribute</SPAN> on the structure or union is to
choose <SPAN style="font-weight: bold;">none</SPAN>. The equivalent for a C code attribute of
<SPAN style="font-style: italic;">#pragma pack()</SPAN> without a value is to specify a <SPAN
style="font-weight: bold;">pack value</SPAN> of <SPAN style="font-weight: bold;">1</SPAN>.
The equivalent of <SPAN style="font-style: italic;"># pragma</SPAN> <SPAN style=
"font-style: italic;">pack(4)</SPAN> is to specify a <SPAN style="font-weight: bold;">pack
value</SPAN> of 4.<BR>
</P>
<DIV style="text-align: center;">
<IMG alt="" src="images/StructureEditorWithFlexArray.png"><BR>
<BR>
</DIV>
@ -334,6 +348,8 @@
table. There are also short-cut keys associated with each of the edit actions.</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<H3><A name="Structure_Editor_Insert_Undefined_Byte"></A> <IMG src="images/Plus.png" alt="">
Insert Undefined Byte</H3>
@ -557,6 +573,7 @@
is created and a component containing it replaces the selected components.</P>
</BLOCKQUOTE><BR>
</BLOCKQUOTE>
<H2><A name="Structure_Editor_Component_Fields"></A>Component Fields</H2>

View file

@ -23,16 +23,17 @@ import java.util.Map.Entry;
import javax.swing.tree.TreePath;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException;
import ghidra.app.plugin.core.datamgr.archive.*;
import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
import ghidra.app.plugin.core.datamgr.util.DataTypeComparator;
import ghidra.app.services.DataTypeManagerService;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.DataTypeArchive;
import ghidra.util.HelpLocation;
import ghidra.util.UniversalID;
class DefaultDataTypeManagerService implements DataTypeManagerService {
// FIXME!! TESTING
public class DefaultDataTypeManagerService implements DataTypeManagerService {
private Map<String, FileDataTypeManager> archiveMap = new HashMap<>();
private DataTypeManager builtInDataTypesManager = BuiltInDataTypeManager.getDataTypeManager();
@ -156,6 +157,9 @@ class DefaultDataTypeManagerService implements DataTypeManagerService {
@Override
public DataType getDataType(TreePath selectedTreeNode) {
if (selectedTreeNode == null) {
return null;
}
throw new UnsupportedOperationException();
}
@ -171,7 +175,11 @@ class DefaultDataTypeManagerService implements DataTypeManagerService {
@Override
public List<DataType> getSortedDataTypeList() {
throw new UnsupportedOperationException();
List<DataType> dataTypes =
builtInDataTypesManager.getDataTypes(BuiltInSourceArchive.INSTANCE);
dataTypes.sort(new DataTypeComparator());
return dataTypes;
// throw new UnsupportedOperationException();
}
@Override

View file

@ -0,0 +1,112 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.compositeeditor;
import java.awt.Component;
import java.awt.Window;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import docking.ActionContext;
import docking.DockingWindowManager;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
/**
* Action for use in the composite data type editor.
* This action has help associated with it.
*/
public class AddBitFieldAction extends CompositeEditorTableAction {
private final static String ACTION_NAME = "Add Bitfield";
private final static String GROUP_NAME = BITFIELD_ACTION_GROUP;
private final static String DESCRIPTION =
"Add a bitfield at the position of a selected component";
private static String[] popupPath = new String[] { ACTION_NAME };
public AddBitFieldAction(CompositeEditorProvider provider) {
super(provider, EDIT_ACTION_PREFIX + ACTION_NAME, GROUP_NAME, popupPath, null, null);
setDescription(DESCRIPTION);
if (!(model instanceof CompEditorModel)) {
throw new AssertException("unsupported use");
}
adjustEnablement();
}
@Override
public void actionPerformed(ActionContext context) {
CompEditorModel editorModel = (CompEditorModel) model;
if (editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) {
return;
}
int rowIndex = model.getSelectedRows()[0];
if (editorModel.isAligned()) {
// Insert before selected component
// ordinal based, user input needed:
// 1. bitfield base datatype
// 2. bitfield size
// 3. bitfield name (can be renamed later)
int ordinal = -1;
DataType baseDataType = null;
if (!editorModel.isAtEnd(rowIndex)) {
DataTypeComponent component = editorModel.getComponent(rowIndex);
ordinal = component.getOrdinal();
if (component.isBitFieldComponent()) {
BitFieldDataType currentBitfield = (BitFieldDataType) component.getDataType();
baseDataType = currentBitfield.getBaseDataType();
}
}
insertBitField(ordinal, baseDataType);
}
else {
BitFieldEditorDialog dlg =
new BitFieldEditorDialog(editorModel.viewComposite, provider.dtmService,
-(rowIndex + 1), ordinal -> refreshTableAndSelection(editorModel, ordinal));
Component c = provider.getComponent();
Window w = SwingUtilities.windowForComponent(c);
DockingWindowManager.showDialog(w, dlg, c);
}
requestTableFocus();
}
private void refreshTableAndSelection(CompEditorModel editorModel, int ordinal) {
editorModel.fireTableDataChanged();
editorModel.compositeInfoChanged();
JTable editorTable = provider.getTable();
editorTable.getSelectionModel().setSelectionInterval(ordinal, ordinal);
}
private void insertBitField(int ordinal, DataType baseDataType) {
// TODO Auto-generated method stub
}
@Override
public void adjustEnablement() {
boolean enabled = true;
CompEditorModel editorModel = (CompEditorModel) model;
if (editorModel.viewComposite == null || editorModel.getNumSelectedRows() != 1 ||
editorModel.isFlexibleArraySelection()) {
enabled = false;
}
setEnabled(enabled);
}
}

View file

@ -0,0 +1,302 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.compositeeditor;
import java.awt.event.MouseEvent;
import javax.swing.Icon;
import javax.swing.JComponent;
import docking.*;
import docking.action.DockingAction;
import docking.action.MenuData;
import ghidra.GhidraApplicationLayout;
import ghidra.app.plugin.core.analysis.DefaultDataTypeManagerService;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.*;
import ghidra.program.model.data.*;
import ghidra.util.SystemUtilities;
import resources.ResourceManager;
public class BitFieldEditorDialog extends DialogComponentProvider {
private static final Icon ADD_ICON = ResourceManager.loadImage("images/Plus.png");
private static final Icon EDIT_ICON = ResourceManager.loadImage("images/move.png");
private static final Icon DELETE_ICON = ResourceManager.loadImage("images/edit-delete.png");
private DataTypeManagerService dtmService;
private Composite composite;
private CompositeChangeListener listener;
private BitFieldEditorPanel bitFieldEditorPanel; // for unaligned use case
BitFieldEditorDialog(Composite composite, DataTypeManagerService dtmService, int editOrdinal,
CompositeChangeListener listener) {
super("Edit " + getCompositeType(composite) + " Bitfield");
this.composite = composite;
this.listener = listener;
this.dtmService = dtmService;
addButtons();
addWorkPanel(buildWorkPanel(editOrdinal));
setRememberLocation(false);
setRememberSize(false);
addActions();
}
private void addButtons() {
addOKButton();
addCancelButton();
if (composite instanceof Structure) {
addApplyButton();
setApplyEnabled(false);
}
}
private static DataTypeComponent getEditComponent(ActionContext context, boolean bitFieldOnly) {
if (!(context instanceof BitFieldEditorPanel.BitFieldEditorContext)) {
return null;
}
BitFieldEditorPanel.BitFieldEditorContext editorContext =
(BitFieldEditorPanel.BitFieldEditorContext) context;
DataTypeComponent dtc = editorContext.getSelectedComponent();
if (dtc != null && (!bitFieldOnly || dtc.isBitFieldComponent())) {
return dtc;
}
return null;
}
private class EditBitFieldAction extends DockingAction {
EditBitFieldAction() {
super("Edit Bitfield", "BitFieldEditorDialog");
setPopupMenuData(new MenuData(new String[] { getName() }, EDIT_ICON));
}
@Override
public void actionPerformed(ActionContext context) {
DataTypeComponent bitfieldDtc = getEditComponent(context, true);
if (bitfieldDtc == null || !bitFieldEditorPanel.endCurrentEdit()) {
return;
}
initEdit(bitfieldDtc.getOrdinal());
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return getEditComponent(context, true) != null;
}
}
private class AddBitFieldAction extends DockingAction {
AddBitFieldAction() {
super("Add Bitfield", "BitFieldEditorDialog");
setPopupMenuData(new MenuData(new String[] { getName() }, ADD_ICON));
}
@Override
public void actionPerformed(ActionContext context) {
if (!bitFieldEditorPanel.endCurrentEdit()) {
return;
}
BitFieldEditorPanel.BitFieldEditorContext editorContext =
(BitFieldEditorPanel.BitFieldEditorContext) context;
bitFieldEditorPanel.initAdd(null, editorContext.getAllocationOffset(),
editorContext.getSelectedBitOffset(), true);
setApplyEnabled(true);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return (context instanceof BitFieldEditorPanel.BitFieldEditorContext) &&
!bitFieldEditorPanel.isAdding();
}
}
private class DeleteComponentAction extends DockingAction {
DeleteComponentAction() {
super("Delete Component", "BitFieldEditorDialog");
setPopupMenuData(new MenuData(new String[] { getName() }, DELETE_ICON));
}
@Override
public void actionPerformed(ActionContext context) {
DataTypeComponent bitfieldDtc = getEditComponent(context, false);
if (bitfieldDtc == null) {
return;
}
int ordinal = bitfieldDtc.getOrdinal();
composite.delete(ordinal);
bitFieldEditorPanel.componentDeleted(ordinal);
if (listener != null) {
listener.componentChanged(ordinal);
}
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return getEditComponent(context, false) != null;
}
}
private void addActions() {
addAction(new AddBitFieldAction());
addAction(new EditBitFieldAction());
addAction(new DeleteComponentAction());
}
@Override
protected void applyCallback() {
if (bitFieldEditorPanel.isEditing() && bitFieldEditorPanel.apply(listener)) {
setApplyEnabled(false);
}
}
@Override
protected void okCallback() {
applyCallback();
close();
}
@Override
public ActionContext getActionContext(MouseEvent event) {
ActionContext context = bitFieldEditorPanel.getActionContext(event);
if (context != null) {
return context;
}
return super.getActionContext(event);
}
@Override
protected void cancelCallback() {
// TODO: Should we cancel without asking?
if (!bitFieldEditorPanel.endCurrentEdit()) {
return;
}
super.cancelCallback();
}
private JComponent buildWorkPanel(int editOrdinal) {
bitFieldEditorPanel = new BitFieldEditorPanel(composite, dtmService);
if (editOrdinal < 0) {
initAdd(-editOrdinal - 1);
}
else {
initEdit(editOrdinal);
}
return bitFieldEditorPanel;
}
private static String getCompositeType(Composite composite) {
// currently supports unaligned case only!
if (composite.isInternallyAligned()) {
throw new IllegalArgumentException("Aligned use not supported");
}
String alignmentMode = composite.isInternallyAligned() ? "Aligned" : "Unaligned";
String type = (composite instanceof Union) ? "Union" : "Structure";
return alignmentMode + " " + type;
}
private void initAdd(int ordinal) {
DataType baseDataType = null;
int offset = 0;
if (ordinal < composite.getNumComponents()) {
DataTypeComponent dtc = composite.getComponent(ordinal);
offset = dtc.getOffset();
if (dtc.isBitFieldComponent()) {
baseDataType = ((BitFieldDataType) dtc.getDataType()).getBaseDataType();
}
}
else if (!composite.isNotYetDefined()) {
offset = composite.getLength();
}
// use previous or default base datatype
bitFieldEditorPanel.initAdd(baseDataType, offset, 0, false);
setApplyEnabled(true);
}
private void initEdit(int editOrdinal) throws ArrayIndexOutOfBoundsException {
DataTypeComponent dtc = composite.getComponent(editOrdinal);
if (!dtc.isBitFieldComponent()) {
throw new IllegalArgumentException("editOrdinal does not correspond to bitfield");
}
bitFieldEditorPanel.initEdit(dtc, getPreferredAllocationOffset(dtc), false);
setApplyEnabled(true);
}
private int getPreferredAllocationOffset(DataTypeComponent bitfieldDtc) {
if (composite instanceof Union) {
return 0;
}
BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType();
int offset = bitfieldDtc.getOffset();
int baseTypeSize = bitfieldDt.getBaseTypeSize();
if (bitfieldDtc.getLength() >= baseTypeSize) {
return offset; // do not adjust
}
DataOrganization dataOrganization = composite.getDataOrganization();
// Assume a reasonable alignment in identifying aligned offset
int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization,
Composite.NOT_PACKING, bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize());
int adjustedOffset = offset - (offset % alignment);
// only adjust if bitfield fits within aligned offset
if (bitfieldDtc.getEndOffset() <= (adjustedOffset + baseTypeSize - 1)) {
return adjustedOffset;
}
return offset;
}
public static void main(String[] args) throws Exception {
//UniversalIdGenerator.initialize();
ApplicationConfiguration configuration = new HeadlessGhidraApplicationConfiguration();
configuration.setInitializeLogging(false);
Application.initializeApplication(new GhidraApplicationLayout(), configuration);
Structure s = new StructureDataType("Foo", 0);
DataTypeComponent dtcA =
s.insertBitFieldAt(0, 4, 16, IntegerDataType.dataType, 4, "BitA", null);
DataTypeComponent dtcZ =
s.insertBitFieldAt(0, 4, 16, IntegerDataType.dataType, 0, "BitZ", null);
DataTypeComponent dtcB =
s.insertBitFieldAt(0, 4, 12, IntegerDataType.dataType, 4, "BitB", null);
DataTypeComponent dtcC =
s.insertBitFieldAt(0, 4, 4, IntegerDataType.dataType, 4, "BitC", null);
DockingWindowManager winMgr = new DockingWindowManager("TEST", null, null);
BitFieldEditorDialog dlg =
new BitFieldEditorDialog(s, new DefaultDataTypeManagerService(), -1, null);
SystemUtilities.runSwingNow(() -> {
winMgr.setVisible(true);
DockingWindowManager.showDialog(null, dlg);
});
}
}

View file

@ -0,0 +1,581 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.compositeeditor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import docking.ActionContext;
import docking.widgets.DropDownSelectionTextField;
import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.compositeeditor.BitFieldPlacementComponent.BitAttributes;
import ghidra.app.plugin.core.compositeeditor.BitFieldPlacementComponent.BitFieldAllocation;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.DataTypeSelectionEditor;
import ghidra.app.util.datatype.NavigationDirection;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.layout.*;
import resources.ResourceManager;
/**
* <code>BitFieldEditorPanel</code> provides the ability to place bitfields
* within unaligned structures and unions.
*/
public class BitFieldEditorPanel extends JPanel {
private static final Icon DECREMENT_ICON = ResourceManager.loadImage("images/Minus.png");
private static final Icon INCREMENT_ICON = ResourceManager.loadImage("images/Plus.png");
private static final String ENTRY_ERROR_DIALOG_TITLE = "Bitfield Entry Error";
private DataTypeManagerService dtmService;
private Composite composite;
private JLabel allocationOffsetLabel;
JButton decrementButton;
JButton incrementButton;
private BitFieldPlacementComponent placementComponent;
private DataType baseDataType;
private DataTypeSelectionEditor dtChoiceEditor;
private JTextField fieldNameTextField;
private SpinnerNumberModel allocSizeModel;
private JSpinnerWithMouseWheel allocSizeInput;
private SpinnerNumberModel bitOffsetModel;
private JSpinnerWithMouseWheel bitOffsetInput;
private SpinnerNumberModel bitSizeModel;
private JSpinnerWithMouseWheel bitSizeInput;
private boolean updating = false;
BitFieldEditorPanel(Composite composite, DataTypeManagerService dtmService) {
super();
this.composite = composite;
if (composite.isInternallyAligned()) {
// A different bitfield editor should be used for aligned composites
throw new IllegalArgumentException("composite must be unaligned");
}
setLayout(new VerticalLayout(5));
setFocusTraversalKeysEnabled(true);
this.dtmService = dtmService;
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
createPlacementPanel();
if (composite instanceof Structure) {
add(createAllocationOffsetPanel());
}
add(placementComponent);
add(createEntryPanel());
enableControls(false);
}
private JPanel createAllocationOffsetPanel() {
JPanel panel = new JPanel(new HorizontalLayout(5));
decrementButton = new JButton(DECREMENT_ICON);
decrementButton.setFocusable(false);
decrementButton.setToolTipText("Decrement allocation unit offset");
decrementButton.addActionListener(e -> adjustAllocationOffset(-1));
panel.add(decrementButton);
incrementButton = new JButton(INCREMENT_ICON);
incrementButton.setFocusable(false);
incrementButton.setToolTipText("Increment allocation unit offset");
incrementButton.addActionListener(e -> adjustAllocationOffset(1));
panel.add(incrementButton);
allocationOffsetLabel = new JLabel();
allocationOffsetLabel.setHorizontalTextPosition(SwingConstants.LEFT);
panel.add(allocationOffsetLabel);
return panel;
}
private void adjustAllocationOffset(int delta) {
int adjustedOffset = placementComponent.getAllocationOffset() + delta;
if (adjustedOffset < 0 || adjustedOffset > composite.getLength()) {
return;
}
placementComponent.setAllocationOffset(adjustedOffset);
updateAllocationOffsetLabel();
}
private void updateAllocationOffsetLabel() {
if (composite instanceof Structure) {
String text =
"Structure Offset of Allocation Unit: " + placementComponent.getAllocationOffset();
allocationOffsetLabel.setText(text);
int offset = placementComponent.getAllocationOffset();
decrementButton.setEnabled(offset > 0);
int length = composite.isNotYetDefined() ? 0 : composite.getLength();
incrementButton.setEnabled(offset < length);
}
}
private JPanel createEntryPanel() {
JComponent baseDataTypeEditor = createDataTypeChoiceEditor();
fieldNameTextField = new JTextField(20);
fieldNameTextField.setFocusable(true);
allocSizeModel = new SpinnerNumberModel(Long.valueOf(4), Long.valueOf(1), Long.valueOf(16),
Long.valueOf(1));
allocSizeInput = new JSpinnerWithMouseWheel(allocSizeModel);
bitOffsetModel = new SpinnerNumberModel(Long.valueOf(0), Long.valueOf(0), Long.valueOf(31),
Long.valueOf(1));
bitOffsetInput = new JSpinnerWithMouseWheel(bitOffsetModel);
bitSizeModel = new SpinnerNumberModel(Long.valueOf(4), Long.valueOf(0), Long.valueOf(4 * 8),
Long.valueOf(1));
bitSizeInput = new JSpinnerWithMouseWheel(bitSizeModel);
allocSizeModel.addChangeListener(e -> update());
bitSizeModel.addChangeListener(e -> update());
bitOffsetModel.addChangeListener(e -> update());
JPanel entryPanel = new JPanel(new TwoColumnPairLayout(5, 15, 5, 0));
entryPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
BorderFactory.createEmptyBorder(5, 5, 5, 5)));
entryPanel.setFocusCycleRoot(true);
entryPanel.add(new JLabel("Base Datatype:"));
entryPanel.add(baseDataTypeEditor);
entryPanel.add(new JLabel("Allocation Bytes:"));
entryPanel.add(allocSizeInput);
entryPanel.add(new JLabel("Field Name:"));
entryPanel.add(fieldNameTextField);
entryPanel.add(new JLabel("Bit Size:"));
entryPanel.add(bitSizeInput);
entryPanel.add(new JPanel());
entryPanel.add(new JPanel());
entryPanel.add(new JLabel("Bit Offset:"));
entryPanel.add(bitOffsetInput);
return entryPanel;
}
private JComponent createDataTypeChoiceEditor() {
dtChoiceEditor = new DataTypeSelectionEditor(dtmService, -1, AllowedDataTypes.BITFIELD_USE);
dtChoiceEditor.setConsumeEnterKeyPress(false);
dtChoiceEditor.setTabCommitsEdit(true);
//dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager());
final DropDownSelectionTextField<DataType> dtChoiceTextField =
dtChoiceEditor.getDropDownTextField();
dtChoiceTextField.setBorder(UIManager.getBorder("TextField.border"));
dtChoiceEditor.addCellEditorListener(new CellEditorListener() {
@Override
public void editingCanceled(ChangeEvent e) {
dtChoiceEditor.setCellEditorValue(baseDataType); // restore
}
@Override
public void editingStopped(ChangeEvent e) {
if (!checkValidBaseDataType()) {
dtChoiceTextField.selectAll();
}
else {
baseDataType = dtChoiceEditor.getCellEditorValueAsDataType();
if (baseDataType != null) {
baseDataType = baseDataType.clone(composite.getDataTypeManager());
}
updateBitSizeModel();
NavigationDirection direction = dtChoiceEditor.getNavigationDirection();
if (direction == NavigationDirection.FORWARD) {
allocSizeInput.requestFocus();
}
else if (direction == NavigationDirection.BACKWARD) {
bitOffsetInput.requestFocus();
}
}
}
});
dtChoiceEditor.getBrowseButton().setFocusable(false);
JComponent editorComponent = dtChoiceEditor.getEditorComponent();
Dimension preferredSize = editorComponent.getPreferredSize();
editorComponent.setPreferredSize(new Dimension(200, preferredSize.height));
return editorComponent;
}
private JPanel createPlacementPanel() {
JPanel midPanel = new JPanel(new PairLayout(5, 5));
JPanel leftMidPanel = new JPanel(new VerticalLayout(13));
leftMidPanel.setBorder(BorderFactory.createEmptyBorder(12, 8, 12, 0));
JLabel byteOffsetLabel = new JLabel("Byte Offset:", SwingConstants.RIGHT);
byteOffsetLabel.setToolTipText("Byte Offset is relative to start of allocation unit");
leftMidPanel.add(byteOffsetLabel);
leftMidPanel.add(new JLabel("Bits:", SwingConstants.RIGHT));
midPanel.add(leftMidPanel);
placementComponent = new BitFieldPlacementComponent(composite);
placementComponent.setFont(UIManager.getFont("TextField.font"));
placementComponent.addMouseWheelListener(e -> bitSizeInput.mouseWheelMoved(e));
placementComponent.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1 &&
bitOffsetInput.isEnabled()) {
setBitFieldOffset(e.getPoint());
}
// if (e.getClickCount() == 2 && endCurrentEdit() &&
// editBitFieldComponent(e.getPoint())) {
// enableControls(true);
// }
}
// public void mousePressed(MouseEvent e) {
// if (e.isPopupTrigger()) {
// setBitFieldPopupContext(e.getPoint());
// }
// };
});
JScrollPane scrollPane =
new JScrollPane(placementComponent, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.getViewport().setBackground(getBackground());
midPanel.add(scrollPane);
return midPanel;
}
private boolean checkValidBaseDataType() {
DropDownSelectionTextField<DataType> textField = dtChoiceEditor.getDropDownTextField();
String dtName = textField.getText().trim();
try {
if (dtName.length() == 0 || !dtChoiceEditor.validateUserSelection()) {
Msg.showError(BitFieldEditorPanel.class, textField, ENTRY_ERROR_DIALOG_TITLE,
"Valid bitfield base datatype entry required");
return false;
}
}
catch (InvalidDataTypeException e) {
Msg.showError(BitFieldEditorPanel.class, textField, ENTRY_ERROR_DIALOG_TITLE,
"Invalid bitfield base datatype: " + e.getMessage());
return false;
}
return true;
}
void initAdd(DataType initialBaseDataType, int allocationOffset, int bitOffset,
boolean useCurrentAllocation) {
if (initialBaseDataType == null) {
initialBaseDataType = baseDataType;
}
if (!BitFieldDataType.isValidBaseDataType(initialBaseDataType)) {
initialBaseDataType = IntegerDataType.dataType.clone(composite.getDataTypeManager());
}
placementComponent.setAllocationOffset(allocationOffset);
long allocationSize = useCurrentAllocation ? (Long) allocSizeModel.getValue()
: initialBaseDataType.getLength();
placementComponent.initAdd((int) allocationSize, 1, bitOffset);
initControls(null, initialBaseDataType);
enableControls(true);
}
/**
* Initialize for edit of existing component or no component if bitfieldDtc is null.
* If null an allocation size of 4-bytes will be used but may be adjusted.
* @param bitfieldDtc bitfield component or null
* @param allocationOffset allocation offset to be used
* @param useCurrentAllocation retain current allocation size, otherwise
* use size of base datatype.
*/
void initEdit(DataTypeComponent bitfieldDtc, int allocationOffset,
boolean useCurrentAllocation) {
String initialFieldName = null;
DataType initialBaseDataType = null;
int allocationSize = -1;
BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation();
if (bitFieldAllocation != null) {
allocationSize = bitFieldAllocation.getAllocationByteSize();
}
if (bitfieldDtc != null) {
if (!bitfieldDtc.isBitFieldComponent()) {
throw new IllegalArgumentException("unsupport data type component");
}
initialFieldName = bitfieldDtc.getFieldName();
BitFieldDataType bitfieldDt = (BitFieldDataType) bitfieldDtc.getDataType();
initialBaseDataType = bitfieldDt.getBaseDataType();
if (!useCurrentAllocation || allocationSize < 1) {
allocationSize = initialBaseDataType.getLength();
}
}
if (allocationSize < 1) {
allocationSize = 4;
}
// TODO: adjust offset and allocationSize if needed
placementComponent.setAllocationOffset(allocationOffset);
placementComponent.init(allocationSize, bitfieldDtc);
initControls(initialFieldName, initialBaseDataType);
enableControls(bitfieldDtc != null);
}
void componentDeleted(int ordinal) {
placementComponent.componentDeleted(ordinal);
}
private void initControls(String initialFieldName, DataType initialBaseDataType) {
updating = true;
try {
baseDataType = initialBaseDataType;
dtChoiceEditor.setCellEditorValue(initialBaseDataType);
fieldNameTextField.setText(initialFieldName);
// Use current placementComponent to obtain initial values
BitFieldAllocation bitFieldAllocation = placementComponent.getBitFieldAllocation();
allocSizeModel.setValue((long) bitFieldAllocation.getAllocationByteSize());
int allocBits = 8 * bitFieldAllocation.getAllocationByteSize();
bitSizeModel.setValue(1L);
bitOffsetModel.setMaximum((long) allocBits - 1);
bitOffsetModel.setValue((long) bitFieldAllocation.getBitOffset());
updateBitSizeModel();
updateAllocationOffsetLabel();
}
finally {
updating = false;
}
}
/**
* @return true if actively editing or adding a bitfield
*/
boolean isEditing() {
return placementComponent.isEditing();
}
/**
* @return true if actively adding a bitfield
*/
boolean isAdding() {
return placementComponent.isAdding();
}
boolean endCurrentEdit() {
if (placementComponent.isEditing()) {
String currentOp = placementComponent.isAdding() ? "add" : "edit";
int option = OptionDialog.showYesNoDialog(this, "Confirm Edit Action",
"Cancel current bitfield " + currentOp + " operation?");
if (option != OptionDialog.YES_OPTION) {
return false;
}
placementComponent.cancelEdit();
enableControls(false);
}
return true;
}
boolean apply(CompositeChangeListener listener) {
boolean deleteConflicts = false;
if (placementComponent.hasApplyConflict()) {
long allocationSize = (Long) allocSizeModel.getValue();
int option = OptionDialog.showOptionDialog(this, "Bitfield Conflict(s)",
"Bitfield placement conflicts with one or more components.\n" +
"Would you like to delete conflicts or move conflicts by " + allocationSize +
" bytes?",
"Delete Conflicts", "Move Conflicts", OptionDialog.WARNING_MESSAGE);
if (option == OptionDialog.CANCEL_OPTION) {
return false;
}
deleteConflicts = (option == OptionDialog.OPTION_ONE);
}
placementComponent.applyBitField(baseDataType, fieldNameTextField.getText().trim(),
deleteConflicts, listener);
enableControls(false);
return true;
}
private void enableControls(boolean enable) {
allocSizeInput.setEnabled(enable);
bitSizeInput.setEnabled(enable);
bitOffsetInput.setEnabled(enable);
if (!enable) {
// TODO: set placementComponent mode to NONE
bitOffsetModel.setValue(0L);
bitSizeModel.setValue(1L);
fieldNameTextField.setText(null);
}
}
private void setBitFieldOffset(Point point) {
int bitOffset = placementComponent.getBitOffset(point);
if (bitOffset >= 0) {
// long cast is required for auto-box to Long object
bitOffsetModel.setValue((long) bitOffset);
}
}
private DataTypeComponent getDataTypeComponent(Point p) {
BitAttributes attrs = placementComponent.getBitAttributes(p);
if (attrs != null) {
return attrs.getDataTypeComponent(true);
}
return null;
}
private void updateBitSizeModel() {
int allocSize = allocSizeModel.getNumber().intValue();
int allocBits = 8 * allocSize;
int baseTypeBits = baseDataType != null ? (8 * baseDataType.getLength()) : allocBits;
long maxBitSize = Math.min(allocBits, baseTypeBits);
bitSizeModel.setMaximum(maxBitSize);
if (maxBitSize < (Long) bitSizeModel.getValue()) {
bitSizeModel.setValue(maxBitSize);
}
}
private void update() {
if (updating) {
return;
}
updating = true;
try {
int allocSize = allocSizeModel.getNumber().intValue();
int allocBits = 8 * allocSize;
updateBitSizeModel();
bitOffsetModel.setMaximum(Long.valueOf(allocBits - 1));
int bitSize = bitSizeModel.getNumber().intValue();
int boff = bitOffsetModel.getNumber().intValue();
int total = bitSize + boff;
if (total > allocBits) {
boff -= total - allocBits;
if (boff < 0) {
boff = 0;
}
}
if (bitSize == 0) {
// force preferred placement of zero-length bit-field
// little-endian: lsb of byte
// big-endian: msb of byte
boff = 8 * (boff / 8);
if (placementComponent.isBigEndian()) {
boff += 7;
}
bitOffsetModel.setStepSize((long) 8);
}
else {
bitOffsetModel.setStepSize((long) 1);
}
bitOffsetModel.setValue(Long.valueOf(boff));
if (bitSize > allocBits) {
bitSize = allocBits;
bitSizeModel.setValue(Long.valueOf(bitSize));
}
placementComponent.refresh(allocSize, bitSize, boff);
}
finally {
updating = false;
}
}
ActionContext getActionContext(MouseEvent event) {
if (placementComponent == event.getSource()) {
Point p = event.getPoint();
return new BitFieldEditorContext(getDataTypeComponent(p),
placementComponent.getBitOffset(p));
}
return null;
}
class BitFieldEditorContext extends ActionContext {
private int selectedBitOffset;
private DataTypeComponent selectedDtc;
private BitFieldEditorContext(DataTypeComponent selectedDtc, int selectedBitOffset) {
this.selectedDtc = selectedDtc;
this.selectedBitOffset = selectedBitOffset;
}
DataTypeComponent getSelectedComponent() {
return selectedDtc;
}
public int getAllocationOffset() {
return placementComponent.getAllocationOffset();
}
public int getSelectedBitOffset() {
return selectedBitOffset;
}
}
private static class JSpinnerWithMouseWheel extends JSpinner implements MouseWheelListener {
JSpinnerWithMouseWheel(SpinnerNumberModel model) {
super(model);
addMouseWheelListener(this);
}
@Override
public void requestFocus() {
DefaultEditor editor = (DefaultEditor) getEditor();
editor.getTextField().requestFocus();
}
@Override
public void mouseWheelMoved(MouseWheelEvent mwe) {
if (!isEnabled()) {
return;
}
if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) {
return;
}
SpinnerNumberModel m = (SpinnerNumberModel) getModel();
if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) {
// TODO: Handle other mouse wheel modes
return;
}
Long value =
mwe.getUnitsToScroll() > 0 ? (Long) m.getPreviousValue() : (Long) m.getNextValue();
if (value != null) {
setValue(value);
mwe.consume();
}
}
}
}

View file

@ -0,0 +1,746 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.compositeeditor;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.HashSet;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
public class BitFieldPlacementComponent extends JPanel {
private static final int CELL_HEIGHT = 30;
private static final int BIT_WIDTH = 10;
private static final int ZERO_BIT_WIDTH = 3;
private static final int BIT_SEPARATOR_THICKNESS = 1;
private static final int BYTE_SEPARATOR_THICKNESS = 2;
private static final int BYTE_WIDTH = 8 * (BIT_WIDTH + BIT_SEPARATOR_THICKNESS);
private static final int SCROLLBAR_THICKNESS = 10;
private static final int MY_HEIGHT = (2 * CELL_HEIGHT) + (3 * BYTE_SEPARATOR_THICKNESS);
private static final Color TEXT_COLOR = Color.black;
private static final Color LINE_COLOR = Color.black;
private static final Color BYTE_HEADER_COLOR = new Color(0xdfdfdf);
private static final Color UNDEFINED_BIT_COLOR = new Color(0xe8e8e8);
private static final Color BITFIELD_BITS_COLOR = Color.green;
private static final Color CONFLICT_BITS_COLOR = Color.yellow;
private static final Color BITFIELD_COMPONENT_COLOR = new Color(0xcfcfff);
private static final Color NON_BITFIELD_COMPONENT_COLOR = new Color(0xafafff);
private static final Color INTERIOR_LINE_COLOR = new Color(0xbfbfbf);
private final Composite composite;
private final boolean bigEndian;
private int allocationOffset;
private BitFieldAllocation bitFieldAllocation;
private EditMode editMode = EditMode.NONE;
private int editOrdinal = -1; // FIXME: improve insert use
BitFieldPlacementComponent(Composite composite) {
this.composite = composite;
bigEndian = composite.getDataOrganization().isBigEndian();
updatePreferredSize();
setSize(getPreferredSize());
setMinimumSize(getPreferredSize());
ToolTipManager.sharedInstance().registerComponent(this);
}
private int getPreferredHeight() {
return MY_HEIGHT + SCROLLBAR_THICKNESS;
}
private int getPreferredWidth() {
if (bitFieldAllocation == null) {
return 10;
}
int extraLineSpace = BYTE_SEPARATOR_THICKNESS - BIT_SEPARATOR_THICKNESS;
return (bitFieldAllocation.allocationByteSize * BYTE_WIDTH) + BYTE_SEPARATOR_THICKNESS +
extraLineSpace;
}
public boolean isBigEndian() {
return bigEndian;
}
public BitFieldAllocation getBitFieldAllocation() {
return bitFieldAllocation;
}
int getBitOffset(Point point) {
int bitWidthWithLine = BIT_WIDTH + BIT_SEPARATOR_THICKNESS;
int cellIndex = (point.x - BYTE_SEPARATOR_THICKNESS) / bitWidthWithLine;
return (8 * bitFieldAllocation.allocationByteSize) - cellIndex - 1;
}
private void updatePreferredSize() {
setPreferredSize(new Dimension(getPreferredWidth(), getPreferredHeight()));
revalidate();
}
void refresh(int allocationByteSize, int bitSize, int bitOffset) {
bitFieldAllocation = new BitFieldAllocation(allocationByteSize, bitSize, bitOffset);
updatePreferredSize();
repaint();
}
void setAllocationOffset(int allocationOffset) {
this.allocationOffset = allocationOffset;
if (bitFieldAllocation != null) {
bitFieldAllocation.refresh();
repaint();
}
}
int getAllocationOffset() {
return allocationOffset;
}
void initAdd(int allocationByteSize, int bitSize, int bitOffset) {
editMode = EditMode.ADD;
editOrdinal = -1;
refresh(allocationByteSize, bitSize, bitOffset);
}
void init(int allocationByteSize, DataTypeComponent editComponent) {
if (editComponent == null) {
editMode = EditMode.NONE;
editOrdinal = -1;
refresh(allocationByteSize, 0, 0);
return;
}
// TODO: consider showing a animated hashed-box around original bit boundary
// of the component being modified
editMode = EditMode.EDIT;
editOrdinal = editComponent.getOrdinal();
BitFieldPlacement placement = new BitFieldPlacement(editComponent, allocationByteSize);
bitFieldAllocation =
new BitFieldAllocation(allocationByteSize, placement.rightBit - placement.leftBit + 1,
(8 * allocationByteSize) - placement.rightBit - 1);
updatePreferredSize();
repaint();
}
boolean hasApplyConflict() {
if (composite instanceof Union) {
return false;
}
for (BitAttributes attrs : bitFieldAllocation.bitAttributes) {
if (attrs.hasConflict() && (attrs.isAddBitField() || attrs.isEditField())) {
return true;
}
}
return false;
}
/**
* @return true if editing or adding a bitfield
*/
boolean isEditing() {
return editMode != EditMode.NONE;
}
/**
* @return true if adding a bitfield
*/
boolean isAdding() {
return editMode == EditMode.ADD;
}
void cancelEdit() {
if (editMode != EditMode.NONE) {
editMode = EditMode.NONE;
editOrdinal = -1;
refresh(bitFieldAllocation.allocationByteSize, 0, 0);
}
}
void componentDeleted(int ordinal) {
if (editMode == EditMode.EDIT) {
if (ordinal == editOrdinal) {
// unexpected removal
editMode = EditMode.ADD;
editOrdinal = -1;
}
else if (ordinal < editOrdinal) {
--editOrdinal;
}
}
bitFieldAllocation.refresh();
repaint();
}
void applyBitField(DataType baseDataType, String fieldName, boolean deleteConflicts,
CompositeChangeListener listener) {
HashSet<Integer> ordinalDeleteSet = new HashSet<>();
if (editOrdinal >= 0) {
composite.delete(editOrdinal);
}
if (deleteConflicts) {
for (BitAttributes attrs : bitFieldAllocation.bitAttributes) {
if (attrs.hasConflict() && (attrs.isAddBitField() || attrs.isEditField())) {
// Edit component will always be on top of conflict
ordinalDeleteSet.add(attrs.getConflict().getOrdinal());
}
}
}
Integer[] ordinalsToDelete = ordinalDeleteSet.toArray(new Integer[ordinalDeleteSet.size()]);
Arrays.sort(ordinalsToDelete); // delete from end first
int ordinal = composite.getNumComponents();
for (int i = ordinalsToDelete.length - 1; i >= 0; i--) {
ordinal = ordinalsToDelete[i];
composite.delete(ordinal);
}
try {
String name = (fieldName != null && fieldName.length() != 0) ? fieldName : null;
DataTypeComponent dtc;
if (composite instanceof Union) {
dtc = composite.insertBitField(ordinal, bitFieldAllocation.allocationByteSize,
bitFieldAllocation.bitOffset, baseDataType, bitFieldAllocation.bitSize, name,
null);
}
else {
Structure struct = (Structure) composite;
dtc = struct.insertBitFieldAt(allocationOffset,
bitFieldAllocation.allocationByteSize, bitFieldAllocation.bitOffset,
baseDataType, bitFieldAllocation.bitSize, name, null);
}
if (listener != null) {
listener.componentChanged(dtc.getOrdinal());
}
}
catch (ArrayIndexOutOfBoundsException | InvalidDataTypeException e) {
Msg.error(this, "Unexpected bitfield apply error", e);
}
finally {
editMode = EditMode.NONE;
editOrdinal = -1;
bitFieldAllocation.refresh();
repaint();
}
}
BitAttributes getBitAttributes(Point p) {
if (bitFieldAllocation == null) {
return null;
}
for (BitAttributes attrs : bitFieldAllocation.bitAttributes) {
if (attrs.rectangle != null && attrs.rectangle.contains(p)) {
return attrs;
}
}
return null;
}
@Override
public String getToolTipText(MouseEvent e) {
BitAttributes attrs = getBitAttributes(e.getPoint());
return attrs != null ? attrs.getTip() : null;
}
@Override
public void paintComponent(Graphics g) {
//super.paintComponent(g);
int height = getHeight();
int width = getWidth();
g.setColor(getBackground());
g.fillRect(0, 0, width, height);
if (bitFieldAllocation == null) {
return;
}
width = getPreferredWidth();
height = MY_HEIGHT;
g.setColor(LINE_COLOR);
g.fillRect(0, 0, width, BYTE_SEPARATOR_THICKNESS); // top line
g.fillRect(0, 0, BYTE_SEPARATOR_THICKNESS, height); // left line (full height)
g.fillRect(width - BYTE_SEPARATOR_THICKNESS, 0, BYTE_SEPARATOR_THICKNESS, height); // right line (full height)
int y = CELL_HEIGHT + BYTE_SEPARATOR_THICKNESS;
g.fillRect(0, y, width, BYTE_SEPARATOR_THICKNESS); // next horizontal line
y += CELL_HEIGHT + BYTE_SEPARATOR_THICKNESS;
g.fillRect(0, y, width, BYTE_SEPARATOR_THICKNESS); // bottom line
paintByteHeader(g, BYTE_SEPARATOR_THICKNESS, allocationOffset);
paintBits((Graphics2D) g, (2 * BYTE_SEPARATOR_THICKNESS) + CELL_HEIGHT);
}
private void paintByteHeader(Graphics g, int y, int baseOffset) {
int byteSize = bitFieldAllocation.allocationByteSize;
int x = BYTE_SEPARATOR_THICKNESS;
for (int i = 0; i < byteSize; i++) {
// last byte header needs to slightly wider
int w = BYTE_WIDTH;
if (i == (byteSize - 1)) {
w += BYTE_SEPARATOR_THICKNESS - BIT_SEPARATOR_THICKNESS;
}
paintByte(g, x, y, w, i, baseOffset);
x += w;
g.fillRect(x - BYTE_SEPARATOR_THICKNESS, y, BYTE_SEPARATOR_THICKNESS, CELL_HEIGHT); // line after each byte
}
}
private void paintByte(Graphics g, int x, int y, int width, int byteIndex, int baseOffset) {
Color curColor = g.getColor();
Font curFont = g.getFont();
int offset = byteIndex;
if (!bigEndian) {
offset = bitFieldAllocation.allocationByteSize - byteIndex - 1;
}
offset += baseOffset;
g.setColor(BYTE_HEADER_COLOR);
g.fillRect(x, y, width - BYTE_SEPARATOR_THICKNESS, CELL_HEIGHT); // byte fill
g.setColor(TEXT_COLOR);
Font textFont = getFont().deriveFont(Font.BOLD);
g.setFont(textFont);
String offsetStr = Integer.toString(offset);
FontMetrics fontMetrics = g.getFontMetrics();
int textY = y + (CELL_HEIGHT + fontMetrics.getMaxAscent()) / 2;
int textX = x + (width - BYTE_SEPARATOR_THICKNESS - fontMetrics.stringWidth(offsetStr)) / 2;
g.drawString(offsetStr, textX, textY);
g.setColor(curColor);
g.setFont(curFont);
}
private void paintBits(Graphics2D g, int y) {
Color curColor = g.getColor();
BitAttributes[] bitAttributes = bitFieldAllocation.bitAttributes;
int x = BYTE_SEPARATOR_THICKNESS;
if (bitAttributes[0] != null && bitAttributes[0].leftEndType == EndBitType.TRUNCATED_END) {
// adjust left-most line to reflect truncated component
x -= BIT_SEPARATOR_THICKNESS; // backup to left line location
drawTruncationLine(g, x, y, CELL_HEIGHT);
x += BIT_SEPARATOR_THICKNESS;
}
BitAttributes prevAttrs = null;
for (int n = 0; n < bitAttributes.length; n++) {
BitAttributes attrs = bitAttributes[n];
boolean paintRightLine = n != (bitAttributes.length - 1);
attrs.paint(g, prevAttrs, paintRightLine);
x += attrs.rectangle.width;
prevAttrs = attrs;
}
if (prevAttrs != null && prevAttrs.rightEndType == EndBitType.TRUNCATED_END) {
x -= BIT_SEPARATOR_THICKNESS; // backup to right line location
drawTruncationLine(g, x, y, CELL_HEIGHT);
}
g.setColor(curColor);
}
private static final Stroke DASH = new BasicStroke(1, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER, 2, new float[] { 3, 3 }, 0);
private void drawTruncationLine(Graphics2D g, int x, int y, int height) {
Color c = g.getColor();
Stroke s = g.getStroke();
g.setColor(getBackground()); // draw over black line
g.setStroke(DASH);
g.drawLine(x, y, x, y + height - 1);
g.setColor(c);
g.setStroke(s);
}
private class BitFieldPlacement {
int leftBit;
int rightBit;
boolean truncateLeft;
boolean truncateRight;
boolean zeroBitField;
BitFieldPlacement(DataTypeComponent component, int allocationByteSize) {
int startOffset = component.getOffset();
int offsetAdjBytes = startOffset - allocationOffset;
if (!bigEndian) {
offsetAdjBytes = allocationByteSize - offsetAdjBytes - component.getLength();
}
int leftAdj = 8 * offsetAdjBytes;
if (component.isBitFieldComponent()) {
BitFieldDataType bitfield = (BitFieldDataType) component.getDataType();
int storageSize = 8 * bitfield.getStorageSize();
rightBit = leftAdj + storageSize - bitfield.getBitOffset() - 1;
// Use effective bit-size since unaligned uses are only concerned with actual
// bits stored (NOTE: this may cause a transition from declared to effective
// bit-size when editing a bitfield where the these bit-sizes differ).
int bitSize = bitfield.getBitSize();
if (bitSize == 0) {
zeroBitField = true;
leftBit = rightBit;
}
else {
leftBit = rightBit - bitSize + 1;
}
}
else {
int componentSize = 8 * component.getLength();
rightBit = leftAdj + componentSize - 1;
leftBit = leftAdj;
}
// System.out.println(component.toString() + " >>> " + leftBit + " - " + rightBit +
// " oa: " + offsetAdjBytes);
// clip to allocation region
int allocBitSize = 8 * allocationByteSize;
truncateRight = false;
if (rightBit >= allocBitSize) {
truncateRight = true;
rightBit = allocBitSize - 1;
}
truncateLeft = false;
if (leftBit < 0) {
truncateLeft = true;
leftBit = 0;
}
}
}
class BitFieldAllocation {
private final int allocationByteSize;
private int bitSize;
private int bitOffset;
private boolean hasConflict;
// bit layout normalized to big-endian layout
// left-most allocation msb has array index of 0
private BitAttributes[] bitAttributes;
BitFieldAllocation(int allocationByteSize, int bitSize, int bitOffset) {
if (allocationByteSize <= 0 || (bitSize + bitOffset) > (8 * allocationByteSize)) {
throw new IllegalArgumentException("allocation size too small");
}
this.allocationByteSize = allocationByteSize;
this.bitSize = bitSize;
this.bitOffset = bitOffset;
refresh();
}
private void refresh() {
allocateBits();
layoutBits();
}
private void allocateBits() {
bitAttributes = new BitAttributes[8 * allocationByteSize];
if (composite instanceof Structure) {
allocateStructureMembers((Structure) composite);
}
if (editMode != EditMode.NONE) {
int rightMostBit = bitAttributes.length - bitOffset - 1;
if (bitSize == 0) {
allocateZeroBitField(null, rightMostBit);
}
else {
int leftMostBit = rightMostBit - bitSize + 1;
allocateBits(null, leftMostBit, rightMostBit, false, false);
}
}
// fill-in unallocated bits
for (int i = 0; i < bitAttributes.length; i++) {
if (bitAttributes[i] == null) {
bitAttributes[i] = new BitAttributes();
}
}
}
private void layoutBits() {
int x = BYTE_SEPARATOR_THICKNESS;
int y = (2 * BYTE_SEPARATOR_THICKNESS) + CELL_HEIGHT;
int width = BIT_WIDTH + BIT_SEPARATOR_THICKNESS;
for (BitAttributes attrs : bitAttributes) {
attrs.layout(x, y, width, CELL_HEIGHT);
x += width;
}
}
private void allocateStructureMembers(Structure struct) {
int allocationEndOffset = allocationOffset + allocationByteSize - 1;
for (DataTypeComponent component : struct.getDefinedComponents()) {
if (component.getOrdinal() == editOrdinal) {
continue;
}
int startOffset = component.getOffset();
int endOffset = component.getEndOffset();
if (endOffset < allocationOffset) {
continue;
}
if (startOffset > allocationEndOffset) {
continue;
}
BitFieldPlacement placement = new BitFieldPlacement(component, allocationByteSize);
if (placement.zeroBitField) {
allocateZeroBitField(component, placement.rightBit);
}
else {
allocateBits(component, placement.leftBit, placement.rightBit,
placement.truncateLeft, placement.truncateRight);
}
}
}
private void allocateBits(DataTypeComponent dtc, int leftBit, int rightBit,
boolean truncatedLeft, boolean truncatedRight) {
if (truncatedLeft && truncatedRight && leftBit == rightBit) {
throw new AssertException();
}
int startIndex = Math.max(0, leftBit);
int endIndex = Math.min(bitAttributes.length - 1, rightBit);
for (int i = startIndex; i <= endIndex; i++) {
EndBitType leftEndType = EndBitType.NOT_END;
EndBitType rightEndType = EndBitType.NOT_END;
if (dtc != null) {
if (i == leftBit) {
leftEndType = truncatedLeft ? EndBitType.TRUNCATED_END : EndBitType.END;
}
if (i == rightBit) {
rightEndType = truncatedLeft ? EndBitType.TRUNCATED_END : EndBitType.END;
}
}
bitAttributes[i] =
new BitAttributes(dtc, leftEndType, rightEndType, bitAttributes[i]);
hasConflict |= bitAttributes[i].hasConflict();
}
}
private void allocateZeroBitField(DataTypeComponent dtc, int bitIndex) {
bitAttributes[bitIndex] = new BitAttributes(dtc, bitAttributes[bitIndex]);
}
public int getAllocationByteSize() {
return allocationByteSize;
}
public int getBitOffset() {
return bitOffset;
}
public int getBitSize() {
return bitSize;
}
}
static enum EditMode {
NONE, ADD, EDIT;
}
static enum EndBitType {
NOT_END, END, TRUNCATED_END;
}
class BitAttributes {
private final DataTypeComponent dtc;
private final EndBitType leftEndType;
private final EndBitType rightEndType;
private final BitAttributes conflict;
private boolean zeroBitfield;
private boolean unallocated;
Rectangle rectangle;
/**
* Unallocated bitfield
* @param dtc
* @param conflict
*/
BitAttributes() {
dtc = null;
leftEndType = EndBitType.NOT_END;
rightEndType = EndBitType.NOT_END;
conflict = null;
unallocated = true;
}
/**
* Zero-length bitfield
* @param dtc
* @param conflict
*/
BitAttributes(DataTypeComponent dtc, BitAttributes conflict) {
this(dtc, dtc != null ? EndBitType.END : EndBitType.NOT_END,
dtc != null ? EndBitType.END : EndBitType.NOT_END, conflict);
zeroBitfield = true;
}
BitAttributes(DataTypeComponent dtc, EndBitType leftEndType, EndBitType rightEndType,
BitAttributes conflict) {
this.dtc = dtc;
this.leftEndType = leftEndType;
this.rightEndType = rightEndType;
this.conflict = conflict;
if (conflict != null) {
leftEndType = conflict.leftEndType;
rightEndType = conflict.rightEndType;
}
}
boolean isAddBitField() {
return !unallocated && dtc == null;
}
boolean isEditField() {
return dtc != null && dtc.getOrdinal() == editOrdinal;
}
boolean hasConflict() {
return getConflict() != null;
}
public DataTypeComponent getConflict() {
BitAttributes c = conflict;
while (c != null && c.dtc.isZeroBitFieldComponent()) {
c = conflict.conflict;
}
return c != null ? c.dtc : null;
}
void layout(int x, int y, int width, int height) {
rectangle = new Rectangle(x, y, width, height);
}
void paint(Graphics g, BitAttributes bitAttrsToLeft, boolean paintRightLine) {
// bit box
Color c = getColor();
g.setColor(c);
g.fillRect(rectangle.x, rectangle.y, BIT_WIDTH, CELL_HEIGHT);
if (zeroBitfield ||
(dtc != null && conflict != null && conflict.dtc.isZeroBitFieldComponent())) {
c = BITFIELD_BITS_COLOR;
Color lineColor = INTERIOR_LINE_COLOR;
if (dtc != null) {
c = BITFIELD_COMPONENT_COLOR;
lineColor = LINE_COLOR;
}
// little-endian: place strip on right-side of bit
// big-endian: place strip on left-side of bit
int xStrip = bigEndian ? rectangle.x : (rectangle.x + BIT_WIDTH - ZERO_BIT_WIDTH);
int xLine =
bigEndian ? (xStrip + ZERO_BIT_WIDTH) : (xStrip - BIT_SEPARATOR_THICKNESS);
g.setColor(c);
g.fillRect(xStrip, rectangle.y, ZERO_BIT_WIDTH, CELL_HEIGHT);
g.setColor(lineColor);
g.fillRect(xLine, rectangle.y, BIT_SEPARATOR_THICKNESS, CELL_HEIGHT);
}
if (bitAttrsToLeft != null && dtc != null && bitAttrsToLeft.unallocated) {
// draw left bit line if we know better than the undefined to our left
g.setColor(LINE_COLOR);
g.fillRect(rectangle.x - BIT_SEPARATOR_THICKNESS, rectangle.y,
BIT_SEPARATOR_THICKNESS, CELL_HEIGHT);
}
if (paintRightLine) {
// draw right bit line
Color lineColor = LINE_COLOR;
if (rightEndType == EndBitType.NOT_END) {
lineColor = INTERIOR_LINE_COLOR;
}
g.setColor(lineColor);
g.fillRect(rectangle.x + BIT_WIDTH, rectangle.y, BIT_SEPARATOR_THICKNESS,
CELL_HEIGHT);
}
}
Color getColor() {
// zero-length stripe will be added later and
// should treated as a conflict
if (unallocated) {
return UNDEFINED_BIT_COLOR;
}
if (conflict != null && !conflict.unallocated) {
if (zeroBitfield) {
return conflict.getColor();
}
if (!conflict.dtc.isZeroBitFieldComponent()) {
return CONFLICT_BITS_COLOR;
}
}
if (zeroBitfield) {
return UNDEFINED_BIT_COLOR;
}
if (dtc == null) {
return BITFIELD_BITS_COLOR; // edit field
}
return dtc.isBitFieldComponent() ? BITFIELD_COMPONENT_COLOR
: NON_BITFIELD_COMPONENT_COLOR;
}
String getTip() {
if (dtc == null) {
return null;
}
String name = dtc.getFieldName();
return dtc.getDataType().getDisplayName() +
(name != null ? (" " + dtc.getFieldName()) : "");
}
DataTypeComponent getDataTypeComponent(boolean ignoreEditComponent) {
if (dtc != null && (dtc.getOrdinal() != editOrdinal || !ignoreEditComponent)) {
return dtc;
}
if (conflict != null) {
return conflict.dtc;
}
return null;
}
}
}

View file

@ -457,8 +457,7 @@ public abstract class CompEditorModel extends CompositeEditorModel {
protected abstract DataTypeComponent insert(int rowIndex, DataType dataType, int length,
String name, String comment) throws InvalidDataTypeException;
protected abstract void insert(int rowIndex, DataType dataType, int length, String name,
String comment, int numCopies) throws InvalidDataTypeException;
protected abstract void insert(int rowIndex, DataType dataType, int length, int numCopies) throws InvalidDataTypeException;
/**
* Add a DataType component into to an editable structure
@ -479,7 +478,7 @@ public abstract class CompEditorModel extends CompositeEditorModel {
return;
}
insert(rowIndex, dataType, dtLen, null, null, multiple);
insert(rowIndex, dataType, dtLen, multiple);
}
/* (non-Javadoc)

View file

@ -0,0 +1,26 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.compositeeditor;
public interface CompositeChangeListener {
/**
* Indicates the ordinal of the component which has been added, updated or cleared.
* @param ordinal component ordinal
*/
void componentChanged(int ordinal);
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,18 +15,18 @@
*/
package ghidra.app.plugin.core.compositeeditor;
public interface EditorAction extends CompositeEditorModelListener {
static final String BASIC_ACTION_GROUP = "1_BASIC_EDITOR_ACTION";
static final String FAVORITES_ACTION_GROUP = "2_FAVORITE_DT_EDITOR_ACTION";
static final String CYCLE_ACTION_GROUP = "3_CYCLE_DT_EDITOR_ACTION";
static final String COMPONENT_ACTION_GROUP = "4_COMPONENT_EDITOR_ACTION";
static final String BITFIELD_ACTION_GROUP = "5_COMPONENT_EDITOR_ACTION";
/**
* Method to set the action's enablement based on the associated editor
* model's current state.
*/
public void adjustEnablement();
}

View file

@ -997,13 +997,14 @@ class StructureEditorModel extends CompEditorModel {
}
@Override
protected void insert(int rowIndex, DataType dataType, int length, String name, String comment,
int numCopies) throws InvalidDataTypeException {
protected void insert(int rowIndex, DataType dataType, int length, int numCopies)
throws InvalidDataTypeException {
checkIsAllowableDataType(dataType, true);
int componentOrdinal = convertRowToOrdinal(rowIndex);
try {
((Structure) viewComposite).insert(componentOrdinal, dataType, length, name, comment,
numCopies);
for (int i = 0; i < numCopies; i++) {
viewComposite.insert(componentOrdinal, dataType, length);
}
if (rowIndex <= row) {
row += numCopies;
}
@ -1026,9 +1027,6 @@ class StructureEditorModel extends CompEditorModel {
return struct.setFlexibleArrayComponent(dt, dtc.getFieldName(), dtc.getComment());
}
/* (non-Javadoc)
* @see ghidra.app.plugin.datamanager.editor.CompositeEditorModel#replace(int, ghidra.program.model.data.DataType, int, java.lang.String, java.lang.String)
*/
@Override
protected DataTypeComponent replace(int rowIndex, DataType dataType, int length, String name,
String comment) throws InvalidDataTypeException {

View file

@ -15,11 +15,10 @@
*/
package ghidra.app.plugin.core.compositeeditor;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.data.Structure;
import javax.swing.ImageIcon;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.data.Structure;
import resources.ResourceManager;
/**
@ -45,44 +44,44 @@ public class StructureEditorProvider extends CompositeEditorProvider {
editorModel.selectionChanged();
}
/* (non-Javadoc)
* @see ghidra.app.plugin.datamanager.editor.EditorProvider#getName()
*/
@Override
public String getName() {
return "Structure Editor";
}
/* (non-Javadoc)
* @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#createActions()
*/
@Override
protected CompositeEditorTableAction[] createActions() {
//@formatter:off
return new CompositeEditorTableAction[] {
new ApplyAction(this),
// new ToggleLockAction(this),
new InsertUndefinedAction(this), new MoveUpAction(this), new MoveDownAction(this),
new ClearAction(this), new DuplicateAction(this), new DuplicateMultipleAction(this),
new DeleteAction(this), new PointerAction(this), new ArrayAction(this),
new ShowComponentPathAction(this), new UnpackageAction(this),
new EditComponentAction(this), new EditFieldAction(this), new HexNumbersAction(this),
new CreateInternalStructureAction(this) };
// new ToggleLockAction(this),
new InsertUndefinedAction(this),
new MoveUpAction(this),
new MoveDownAction(this),
new ClearAction(this),
new DuplicateAction(this),
new DuplicateMultipleAction(this),
new DeleteAction(this),
new PointerAction(this),
new ArrayAction(this),
new ShowComponentPathAction(this),
new UnpackageAction(this),
new EditComponentAction(this),
new EditFieldAction(this),
new HexNumbersAction(this),
new CreateInternalStructureAction(this),
new AddBitFieldAction(this)
};
//@formatter:on
}
/* (non-Javadoc)
* @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpName()
*/
@Override
public String getHelpName() {
return "Structure_Editor";
}
/* (non-Javadoc)
* @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpTopic()
*/
@Override
public String getHelpTopic() {
return "DataTypeEditors";
}
}

View file

@ -410,10 +410,10 @@ class UnionEditorModel extends CompEditorModel {
}
@Override
public void insert(int rowIndex, DataType dataType, int length, String name, String comment,
int numCopies) throws InvalidDataTypeException {
public void insert(int rowIndex, DataType dataType, int length, int numCopies)
throws InvalidDataTypeException {
for (int ii = 0; ii < numCopies; ++ii) {
insert(rowIndex + ii, dataType, length, name, comment);
insert(rowIndex + ii, dataType, length, null, null);
}
}

View file

@ -15,11 +15,10 @@
*/
package ghidra.app.plugin.core.compositeeditor;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.data.Union;
import javax.swing.ImageIcon;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.data.Union;
import resources.ResourceManager;
/**
@ -49,32 +48,34 @@ public class UnionEditorProvider extends CompositeEditorProvider {
return "Union Editor";
}
/* (non-Javadoc)
* @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#createActions()
*/
@Override
protected CompositeEditorTableAction[] createActions() {
return new CompositeEditorTableAction[] { new ApplyAction(this), new MoveUpAction(this),
new MoveDownAction(this), new DuplicateAction(this), new DuplicateMultipleAction(this),
new DeleteAction(this), new PointerAction(this), new ArrayAction(this),
new ShowComponentPathAction(this), new EditComponentAction(this),
new EditFieldAction(this), new HexNumbersAction(this) };
//@formatter:off
return new CompositeEditorTableAction[] {
new ApplyAction(this),
new MoveUpAction(this),
new MoveDownAction(this),
new DuplicateAction(this),
new DuplicateMultipleAction(this),
new DeleteAction(this),
new PointerAction(this),
new ArrayAction(this),
new ShowComponentPathAction(this),
new EditComponentAction(this),
new EditFieldAction(this),
new HexNumbersAction(this),
new AddBitFieldAction(this)
};
//@formatter:on
}
/* (non-Javadoc)
* @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpName()
*/
@Override
public String getHelpName() {
return "Structure_Editor";
}
/* (non-Javadoc)
* @see ghidra.app.plugin.compositeeditor.CompositeEditorProvider#getHelpTopic()
*/
@Override
public String getHelpTopic() {
return "DataTypeEditors";
}
}

View file

@ -71,7 +71,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
private GoToService goToService;
private DockingAction addAction;
private DockingAction deleteAction;
private DataTypeManager dataTypeManager;
private LayeredDataTypeManager dataTypeManager;
private Program activeProgram;
private SwingUpdateManager updateManager = new SwingUpdateManager(650, () -> updatePreview());
@ -156,7 +156,33 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
updateModel();
}
void updateModel() {
private List<DataTypePath> getModelDataTypePaths() {
// retain order as they currently exist within model
List<DataTypePath> list = new ArrayList<>();
for (Preview preview : model.getModelData()) {
if (preview instanceof DataTypePreview) {
list.add(preview.getDataType().getDataTypePath());
}
else if (preview instanceof DataTypeComponentPreview) {
DataTypeComponentPreview componentPreview = (DataTypeComponentPreview) preview;
if (componentPreview.getParent() == null) {
list.add(preview.getDataType().getDataTypePath());
}
}
}
return list;
}
private void updateModel() {
// NOTE: data types do not respond to switching the data organization object
// since this is cached internal to the data type at time of construction.
// We must purge old datatypes and have them re-instantiated by the
// datatype manager
List<DataTypePath> dtPaths = getModelDataTypePaths();
model.removeAll();
dataTypeManager.invalidate();
int transactionId = dataTypeManager.startTransaction("realign");
try {
Iterator<Composite> allComposites = dataTypeManager.getAllComposites();
@ -164,14 +190,19 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
Composite composite = allComposites.next();
if (composite.isInternallyAligned()) {
composite.realign();
model.removeAll(composite);
model.add(composite);
}
}
}
finally {
dataTypeManager.endTransaction(transactionId, true);
}
for (DataTypePath dtPath : dtPaths) {
DataType dataType = dataTypeManager.getDataType(dtPath);
if (dataType != null) {
model.add(dataType);
}
}
}
@Override
@ -693,5 +724,9 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
return super.getDataOrganization();
}
void invalidate() {
invalidateCache();
}
}
}

View file

@ -192,7 +192,7 @@ public abstract class BiDirectionDataType extends StructureDataType
protected void shiftOffsets(int startIndex, int endIndex, int deltaOrdinal, int deltaOffset) {
for (int i = startIndex; i <= endIndex && i < components.size(); i++) {
DataTypeComponentImpl dtc = components.get(i);
shiftOffsets(dtc, deltaOrdinal, deltaOffset);
shiftOffset(dtc, deltaOrdinal, deltaOffset);
}
}
@ -242,7 +242,7 @@ public abstract class BiDirectionDataType extends StructureDataType
}
@Override
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length,
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
String newName, String comment) {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
throw new IllegalArgumentException(
@ -297,11 +297,6 @@ public abstract class BiDirectionDataType extends StructureDataType
return dtc;
}
@Override
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length) {
return insertAtOffset(offset, dataType, length, null, null);
}
@Override
public DataTypeComponent add(DataType dataType, int length, String newName, String comment) {
return addPositive(dataType, length, newName, comment);
@ -374,7 +369,7 @@ public abstract class BiDirectionDataType extends StructureDataType
structLength += absAmount;
notifySizeChanged();
}
@Override
public DataTypeComponent insert(int index, DataType dataType, int length, String newName,
String comment) {
@ -407,12 +402,6 @@ public abstract class BiDirectionDataType extends StructureDataType
// return dtc;
}
@Override
public void insert(int ordinal, DataType dataType, int length, String name, String comment,
int numCopies) {
throw new AssertException("BiDirectionDataType.insert() not implemented.");
}
protected void insertAtOffset(int offset, int numBytes) {
if (offset < splitOffset - negativeLength || offset > splitOffset + positiveLength) {
throw new IllegalArgumentException("Offset " + offset +
@ -676,7 +665,7 @@ public abstract class BiDirectionDataType extends StructureDataType
// dtMgr.dataTypeChanged(this);
// }
}
@Override
public DataTypeComponent[] getDefinedComponents() {
return components.toArray(new DataTypeComponent[components.size()]);
@ -704,12 +693,6 @@ public abstract class BiDirectionDataType extends StructureDataType
return replace(origDtc, dataType, length, newName, comment);
}
@Override
public DataTypeComponent replace(int index, DataType dataType, int length) {
validateDataType(dataType);
return replace(index, dataType, length, null, null);
}
@Override
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length,
String newName, String comment) {

View file

@ -24,8 +24,6 @@ import ghidra.program.model.pcode.Varnode;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import java.lang.UnsupportedOperationException;
public class StackPieceDataType extends DataTypeImpl {
private final Variable variable;
@ -66,13 +64,13 @@ public class StackPieceDataType extends DataTypeImpl {
}
@Override
public void setName(String name) throws InvalidNameException, DuplicateNameException {
public void setName(String name) throws InvalidNameException {
throw new UnsupportedOperationException();
}
@Override
public void setNameAndCategory(CategoryPath path, String name) throws InvalidNameException,
DuplicateNameException {
public void setNameAndCategory(CategoryPath path, String name)
throws InvalidNameException, DuplicateNameException {
throw new UnsupportedOperationException();
}

View file

@ -22,7 +22,7 @@ import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
class MSRichProductBuildNumberDataType extends DataTypeImpl {
private final CompId compid;
public MSRichProductBuildNumberDataType(CompId compid) {
@ -58,7 +58,7 @@ class MSRichProductBuildNumberDataType extends DataTypeImpl {
}
@Override
public void setName(String name) throws InvalidNameException, DuplicateNameException {
public void setName(String name) throws InvalidNameException {
// ignored
}

View file

@ -58,7 +58,7 @@ class MSRichProductIDDataType extends DataTypeImpl {
}
@Override
public void setName(String name) throws InvalidNameException, DuplicateNameException {
public void setName(String name) throws InvalidNameException {
// ignored
}
@ -92,7 +92,7 @@ class MSRichProductIDDataType extends DataTypeImpl {
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
return compid.getProductDescription();
}
@Override

View file

@ -58,7 +58,7 @@ class RichObjectCountDataType extends DataTypeImpl {
}
@Override
public void setName(String name) throws InvalidNameException, DuplicateNameException {
public void setName(String name) throws InvalidNameException {
// ignored
}

View file

@ -27,11 +27,8 @@ import ghidra.program.model.data.*;
*/
public class CompositeHandler {
private DataType lastBitFieldType = null; // type that has a bitfield
private int bitLength = 0; // how many bits have been used
private int anonCnt = 0; // count of anonymous unions created
private Composite parent; // parent container for bitfields
private Composite bitFieldUnion; // artificial union to contain subfields
public CompositeHandler(Composite parent) {
super();
@ -42,7 +39,7 @@ public class CompositeHandler {
return parent;
}
public void add(Declaration dec) {
public void add(Declaration dec) throws IllegalArgumentException {
if (dec == null || dec.getDataType() == null) {
return;
}
@ -52,7 +49,6 @@ public class CompositeHandler {
}
// not a bitfield, just add the data type to composite
if (!dec.isBitField()) {
initialize();
if (dec.isFlexArray() && parent instanceof Structure) {
((Structure) parent).setFlexibleArrayComponent(dec.getDataType(), dec.getName(),
dec.getComment());
@ -62,72 +58,16 @@ public class CompositeHandler {
return;
}
// add bit-field component
DataType dataType = dec.getDataType();
int bitSize = dec.getBitFieldSize();
// if data type different, start new subfield
handleFullBitfieldUnion(dataType, bitSize);
// add the bitfield to the continer union
String bitoff =
(bitSize == 1 ? "" + bitLength : bitLength + "-" + (bitLength + bitSize - 1));
bitFieldUnion.add(dataType, dec.getName(), ": bits " + bitoff);
lastBitFieldType = dataType;
bitLength += bitSize;
try {
parent.addBitField(dataType, dec.getBitFieldSize(), dec.getName(), dec.getComment());
}
catch (InvalidDataTypeException e) {
// TODO Auto-generated catch block
throw new IllegalArgumentException(
"Invalid bitfield " + dec.getName() + " : " + dec.getBitFieldSize());
}
}
/**
* Creates a new bitfield union container if one not created yet or the current
* is full.
*
* @param dataType - type that is about to be added to container
* @param bitSize
*/
private void handleFullBitfieldUnion(DataType dataType, int bitSize) {
if (!bitfieldFull(dataType, bitSize)) {
return;
}
// create an anonymous union to hold sub bitfields
bitFieldUnion = new UnionDataType(parent.getCategoryPath(),
"anon_" + parent.getName() + "_bitfield_" + ++anonCnt);
bitFieldUnion = (Composite) parent.add(bitFieldUnion).getDataType();
bitLength = 0;
}
/**
* Check if a new union needs to be created
*
* @param dataType type that will be added to the union
* @param bitSize size of the bitfield to be added
*
* @return true if a new bitfied union needs to be added
*/
private boolean bitfieldFull(DataType dataType, int bitSize) {
if (parent instanceof Union) {
bitFieldUnion = parent;
return false;
}
// no union yet
if (bitFieldUnion == null) {
return true;
}
// datatype has changed
if (!dataType.equals(lastBitFieldType)) {
return true;
}
// union has overflowed
return (bitLength + bitSize) > (dataType.getLength() * 8);
}
/**
* Clears any residual bitfield info so a new bitfield container will
* be created when necessary.
*/
private void initialize() {
lastBitFieldType = null;
bitLength = 0;
bitFieldUnion = null;
}
}

View file

@ -26,7 +26,7 @@ public class Declaration {
private DataType dt;
private String name;
private String comment;
private int bitSize = 0;
private int bitSize = -1;
private boolean flexArray = false; // true if this is a zero size flex array component
public Declaration() {
@ -97,9 +97,6 @@ public class Declaration {
if (name == null) {
return "";
}
if (isBitField()) {
return name + ":" + bitSize;
}
return name;
}
@ -133,7 +130,7 @@ public class Declaration {
* @return true if a bitfield size has been set
*/
boolean isBitField() {
return bitSize != 0;
return bitSize >= 0;
}
/**

View file

@ -15,14 +15,6 @@
*/
package ghidra.app.util.datatype;
import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.*;
import ghidra.util.data.DataTypeParser;
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
@ -31,6 +23,12 @@ import javax.swing.tree.TreePath;
import docking.options.editor.ButtonPanelFactory;
import docking.widgets.DropDownSelectionTextField;
import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.data.*;
import ghidra.util.data.DataTypeParser;
/**
* An editor that is used to show the {@link DropDownSelectionTextField} for the entering of
@ -58,6 +56,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
private JPanel editorPanel;
private DropDownSelectionTextField<DataType> selectionField;
private JButton browseButton;
private DataTypeManagerService dataTypeManagerService;
private int maxSize = -1;
private DataTypeManager dataTypeManager;
@ -109,9 +108,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
}
private void init() {
selectionField =
new DropDownSelectionTextField<DataType>(new DataTypeDropDownSelectionDataModel(
dataTypeManagerService));
selectionField = new DropDownSelectionTextField<>(
new DataTypeDropDownSelectionDataModel(dataTypeManagerService));
selectionField.addCellEditorListener(new CellEditorListener() {
@Override
public void editingCanceled(ChangeEvent e) {
@ -128,15 +126,9 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
selectionField.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
JButton browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE);
browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE);
browseButton.setToolTipText("Browse the Data Manager");
browseButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// show the data type manager
showDataTypeBrowser();
}
});
browseButton.addActionListener(e -> showDataTypeBrowser());
editorPanel = new JPanel();
editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS));
@ -196,7 +188,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
* Returns the component that allows the user to edit.
* @return the component that allows the user to edit.
*/
public Component getEditorComponent() {
public JComponent getEditorComponent() {
return editorPanel;
}
@ -204,6 +196,10 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
return selectionField;
}
public JButton getBrowseButton() {
return browseButton;
}
/**
* Sets the initially selected node in the data type tree that the user can choose to
* show.
@ -321,11 +317,11 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// look for the case where the user made a selection from the matching window, but
// then changed the text field text.
DataType selectedDataType = selectionField.getSelectedValue();
if (selectedDataType != null && selectionField.getText().equals(selectedDataType.getName())) {
if (selectedDataType != null &&
selectionField.getText().equals(selectedDataType.getName())) {
DataTypeParser.ensureIsAllowableType(selectedDataType, allowedDataTypes);
return true;
}
return false;
}
@ -346,6 +342,11 @@ public class DataTypeSelectionEditor extends AbstractCellEditor {
// TODO: implement in the future to allow the user to create data types
private boolean promptUserToCreateDataType() throws InvalidDataTypeException {
if (selectionField.getText().trim().length() == 0) {
// no need to invoke parser on empty string
return false;
}
// we will create new pointer and array types by default
DataType newDataType = null;
// try {

View file

@ -81,10 +81,10 @@ public class MUIResourceDataType extends DynamicDataType {
if (checkMagic(memBuffer)) {
StructureDataType sdt = MUIStructureHeader();
tempOffset = addComp(sdt, sdt.getLength(), "muiResourceHeader",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
sdt = MUIStructureData(tempOffset, memBuffer, offsets, sizes);
tempOffset = addComp(sdt, sdt.getLength(), "muiResourceData",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
else {
Msg.debug(this, "Not an MUI resource data type at " + mbIn.getAddress());
@ -107,7 +107,7 @@ public class MUIResourceDataType extends DynamicDataType {
"0x01 = internal, 0x02 = external");
ArrayDataType adt16 = new ArrayDataType(ByteDataType.dataType, 16, 1);
ArrayDataType adt24 = new ArrayDataType(ByteDataType.dataType, 16, 1);
ArrayDataType adt24 = new ArrayDataType(ByteDataType.dataType, 24, 1);
struct.add(adt16, 16, "serviceChecksum", "");
struct.add(adt16, 16, "checksum", "");
@ -195,9 +195,8 @@ public class MUIResourceDataType extends DynamicDataType {
private int addComp(DataType dataType, int length, String fieldName, Address address,
List<DataTypeComponent> comps, int currentOffset) {
if (length > 0) {
ReadOnlyDataTypeComponent readOnlyDataTypeComponent =
new ReadOnlyDataTypeComponent(dataType, this, length, comps.size(), currentOffset,
fieldName, null);
ReadOnlyDataTypeComponent readOnlyDataTypeComponent = new ReadOnlyDataTypeComponent(
dataType, this, length, comps.size(), currentOffset, fieldName, null);
comps.add(readOnlyDataTypeComponent);
currentOffset += length;
}

View file

@ -308,7 +308,7 @@ public class BytesFieldFactory extends FieldFactory {
}
alignSize = (int) (nextComponent.getMinAddress().subtract(data.getMaxAddress())) - 1;
}
if (alignSize == 0) {
if (alignSize <= 0) {
return null;
}
int alignmentOffset = data.getParentOffset() + data.getLength();

View file

@ -271,8 +271,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
int offset = (int) address.subtract(parent.getMinAddress());
data = parent.getComponentAt(offset);
// Need to handle filler in aligned structures in a special way.
if (data == null && ((Structure) dt).isInternallyAligned()) {
// Need to handle filler in a special way.
if (data == null) {
// So look for next non-filler address.
offset++;
int length = dt.getLength();
@ -280,7 +280,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
// If not beyond structure's end, check for non-filler.
data = parent.getComponentAt(offset);
if (data != null) { // Found non filler address so return it.
return parent.getMinAddress().add(offset);
return data.getMinAddress();
}
}
}
@ -315,9 +315,12 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
}
else {
// otherwise just return the next dataComponent after this one
if (index < parent.getNumComponents() - 1) {
return parent.getComponent(index + 1).getMinAddress();
while (index < parent.getNumComponents() - 1) {
index++;
Data component = parent.getComponent(index);
if (address.compareTo(component.getMinAddress()) < 0) {
return component.getAddress();
}
}
}
return null;
@ -369,7 +372,9 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
else {
int offset = (int) addr.subtract(parent.getMinAddress());
data = parent.getComponentAt(offset - 1);
List<Data> componentsContaining = parent.getComponentsContaining(offset - 1);
data = componentsContaining.isEmpty() ? null
: componentsContaining.get(componentsContaining.size() - 1);
}
if (data == null) {
return addr.previous();
@ -415,14 +420,18 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
}
}
else {
Data tmpData = data.getComponentAt((int) addr.subtract(dataAddr));
if (tmpData != null) {
if (tmpData.getMinAddress().equals(addr)) {
list.add(tmpData);
}
if (tmpData.getNumComponents() > 0) {
addOpenData(list, tmpData, addr);
else { // Structure
List<Data> dataList = data.getComponentsContaining((int) addr.subtract(dataAddr));
if (dataList != null) { // nested flex-arrays can cause odd behavior
for (Data subData : dataList) {
// The only case where more than one subData exists is for bit-fields.
// Depending upon the packing, bit-fields at different offsets may overlap
if (subData.getMinAddress().equals(addr)) {
list.add(subData);
}
if (subData.getNumComponents() > 0) {
addOpenData(list, subData, addr);
}
}
}
}

View file

@ -15,7 +15,8 @@
*/
package ghidra.util.data;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.services.DataTypeManagerService;
@ -45,7 +46,12 @@ public class DataTypeParser {
/**
* Only Fixed-length data types and string data types
*/
STRINGS_AND_FIXED_LENGTH
STRINGS_AND_FIXED_LENGTH,
/**
* Only Enums, Integer types and those Typedefs based on them
* for use as a bitfield base datatype
*/
BITFIELD_USE
}
private DataTypeManager sourceDataTypeManager; // may be null
@ -57,7 +63,7 @@ public class DataTypeParser {
* A constructor that does not use the source or destination data type managers. In terms of
* the source data type manager, this means that all data type managers will be used when
* resolving data types.
*
*
* @param dataTypeManagerService
* @param allowedTypes
*/
@ -73,7 +79,7 @@ public class DataTypeParser {
* @param destinationDataTypeManager target data-type manager, or null
* @param dataTypeManagerService data-type manager tool service, or null
* @param allowedTypes constrains which data-types may be parsed
*
*
* @see #DataTypeParser(DataTypeManagerService, AllowedDataTypes)
*/
public DataTypeParser(DataTypeManager sourceDataTypeManager,
@ -118,7 +124,7 @@ public class DataTypeParser {
* Parse a data-type string specification using the specified baseDatatype.
* @param suggestedBaseDataType base data-type (may be null), this will be used as the base data-type if
* its name matches the base name in the specified dataTypeString.
* @param dataTypeString a base data-type followed by a sequence of zero or more pointer/array decorations to be applied.
* @param dataTypeString a base data-type followed by a sequence of zero or more pointer/array decorations to be applied.
* The string may start with the baseDataType's name.
* @return parsed data-type or null if not found
* @throws InvalidDataTypeException if data-type string is invalid or length exceeds specified maxSize
@ -180,6 +186,12 @@ public class DataTypeParser {
throw new InvalidDataTypeException("fixed-length or string data-type required");
}
break;
case BITFIELD_USE:
if (!BitFieldDataType.isValidBaseDataType(dt)) {
throw new InvalidDataTypeException(
"enum or integer derived data-type required");
}
break;
case ALL:
// do nothing
break;
@ -334,8 +346,7 @@ public class DataTypeParser {
// see if one of the data types belongs to the program or the built in types, where the
// program is more important than the builtin
for (Iterator<DataType> iter = dtList.iterator(); iter.hasNext();) {
DataType dataType = iter.next();
for (DataType dataType : dtList) {
DataTypeManager manager = dataType.getDataTypeManager();
if (manager instanceof BuiltInDataTypeManager) {
programDataType = dataType;
@ -350,8 +361,7 @@ public class DataTypeParser {
return null;
}
for (Iterator<DataType> iter = dtList.iterator(); iter.hasNext();) {
DataType dataType = iter.next();
for (DataType dataType : dtList) {
// just one non-matching case means that we can't use the program's data type
if (!programDataType.isEquivalent(dataType)) {
return null;

View file

@ -1674,17 +1674,19 @@ Composite StructOrUnion() : {Composite comp;}
{
(
<STRUCT> ( DeclSpec() )* { comp = new StructureDataType(ANONYMOUS_STRUCT_PREFIX + cnt++, 0);
try {
// always set the packing, because by default structures should be aligned
// Always set the packing, because by default structures should be aligned
// setting 0 turns off packing, but sets structures to be aligned
// TODO: is this correct, should this be changed and controlled by the
// compiler spec.
comp.setPackingValue(this.packSize);
} catch (InvalidInputException e1) {
e1.printStackTrace(); }
}
|
<UNION> ( DeclSpec() )* { comp = new UnionDataType(ANONYMOUS_UNION_PREFIX + cnt++); }
<UNION> ( DeclSpec() )* { comp = new UnionDataType(ANONYMOUS_UNION_PREFIX + cnt++);
// Always set the packing, because by default structures should be aligned
// setting 0 turns off packing, but sets structures to be aligned.
comp.setPackingValue(this.packSize);
}
)
{
try {
@ -1813,7 +1815,14 @@ void StructDeclarator(Declaration dt, Composite comp, CompositeHandler composite
}
}]
|
":" ConstantExpression()
":" bitSizeObj = ConstantExpression() {
Integer bitSize = getConstantValue(bitSizeObj,0);
if (bitSize != 0) {
throw new ParseException("invalid bit-field declaration: ':" + bitSize);
}
dec = new Declaration(dt);
dec.setBitFieldSize(0);
}
)
{
try {

View file

@ -51,8 +51,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
super();
}
@Before
public void setUp() throws Exception {
@Before
public void setUp() throws Exception {
builder = new ToyProgramBuilder("Call Node Test", true);
builder.createMemory(".text", "0x0", 0x3000);
@ -64,8 +64,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
node = new OutgoingFunctionCallNode(program, function, source, true, new AtomicInteger(5));
}
@Test
public void testGenerateChildren_SelfRecursiveCall() throws Exception {
@Test
public void testGenerateChildren_SelfRecursiveCall() throws Exception {
builder.createMemoryCallReference(nodeAddress, nodeAddress);
@ -73,8 +73,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_CalledFunctionExists() throws Exception {
@Test
public void testGenerateChildren_CalledFunctionExists() throws Exception {
String otherAddress = "0x1000";
builder.createEmptyFunction("Function_2", otherAddress, 10, DataType.DEFAULT);
builder.createMemoryCallReference(nodeAddress, otherAddress);
@ -84,8 +84,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertEquals("Function_2", children.get(0).getName());
}
@Test
public void testGenerateChildren_CalledFunctionExists_ExternalCall() throws Exception {
@Test
public void testGenerateChildren_CalledFunctionExists_ExternalCall() throws Exception {
String otherAddress = "0x1000";
String externalFunctionName = "External_Function";
@ -100,8 +100,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertEquals(externalFunctionName, children.get(0).getName());
}
@Test
public void testGenerateChildren_CallReference_ExternalFunction_NoFunctionInMemory()
@Test
public void testGenerateChildren_CallReference_ExternalFunction_NoFunctionInMemory()
throws Exception {
builder.createMemoryCallReference(nodeAddress, "EXTERNAL:00000001");
@ -111,8 +111,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertEquals("EXTERNAL:00000001", children.get(0).getName());
}
@Test
public void testGenerateChildren_CallReference_ToPointer_ToExternalFunction() throws Exception {
@Test
public void testGenerateChildren_CallReference_ToPointer_ToExternalFunction() throws Exception {
//
// Function A
@ -137,8 +137,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertEquals(externalFunctionName, children.get(0).getName());
}
@Test
public void testGenerateChildren_CallReference_ToPointer_ToNonExternalFunction()
@Test
public void testGenerateChildren_CallReference_ToPointer_ToNonExternalFunction()
throws Exception {
//
@ -162,8 +162,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.get(0) instanceof DeadEndNode);
}
@Test
public void testGenerateChildren_CallReference_ToPointer_Offcut() throws Exception {
@Test
public void testGenerateChildren_CallReference_ToPointer_Offcut() throws Exception {
//
// Bad code case; expected reference to pointer, but no data there
@ -179,21 +179,22 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.get(0) instanceof DeadEndNode);
}
@Test
public void testGenerateChildren_WriteReference() throws Exception {
@Test
public void testGenerateChildren_WriteReference() throws Exception {
//
// Have a reference in the function to a place that is not another function, and the
// reference is a write reference. No call node is created.
//
builder.createMemoryReference(nodeAddress, "0x1000", RefType.WRITE, SourceType.USER_DEFINED);
builder.createMemoryReference(nodeAddress, "0x1000", RefType.WRITE,
SourceType.USER_DEFINED);
List<GTreeNode> children = node.generateChildren(TaskMonitor.DUMMY);
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_ReadReference_NullInstruction() throws Exception {
@Test
public void testGenerateChildren_ReadReference_NullInstruction() throws Exception {
//
// Have a reference in the function to a place that is not another function, and the
@ -208,8 +209,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
}
@Test
public void testGenerateChildren_ReadReference_NotCallInstruction() throws Exception {
@Test
public void testGenerateChildren_ReadReference_NotCallInstruction() throws Exception {
//
// Read reference to an instruction with a flow type that is not a call
@ -223,8 +224,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_ReadReference_CallInstruction_InstructionAtToAddress()
@Test
public void testGenerateChildren_ReadReference_CallInstruction_InstructionAtToAddress()
throws Exception {
//
@ -243,8 +244,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_NoReference()
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_NoReference()
throws Exception {
//
@ -258,8 +259,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_NonExternalReference()
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_NonExternalReference()
throws Exception {
//
@ -277,8 +278,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_NonFunctionSymbol()
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_NonFunctionSymbol()
throws Exception {
//
@ -296,8 +297,8 @@ public class OutgoingFunctionCallNodeTest extends AbstractGenericTest {
assertTrue(children.isEmpty());
}
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_FunctionSymbol()
@Test
public void testGenerateChildren_ReadReference_CallInstruction_ToData_ExternalReference_FunctionSymbol()
throws Exception {
//

View file

@ -28,6 +28,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite.AlignmentType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.UsrException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
@ -118,11 +119,16 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
@Test
public void testEditedDtCategoryRemoved() throws Exception {
DataTypeManager dtm = complexUnion.getDataTypeManager();
Union refUnion = (Union) dtm.getDataType("/testCat/refUnion");
assertNotNull(refUnion);
Category tempCat;
try {
startTransaction("Modify Program");
tempCat = pgmRootCat.createCategory("Temp");
tempCat.moveDataType(complexUnion, DataTypeConflictHandler.DEFAULT_HANDLER);
tempCat.moveDataType(refUnion, DataTypeConflictHandler.DEFAULT_HANDLER);
}
finally {
endTransaction(true);
@ -142,7 +148,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
});
waitForSwing();
// refUnion* gets removed since it has only a complexUnion* that was removed.
// refUnion* gets removed
assertEquals(num - 1, model.getNumComponents());
assertTrue(dt18.isEquivalent(getDataType(18)));
assertTrue(dt20.isEquivalent(getDataType(19)));
@ -409,16 +415,31 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest {
init(complexUnion, pgmTestCat, false);
int num = model.getNumComponents();
// Clone the data types we want to hold onto for comparison later, since reload can close the viewDTM.
DataType dt18 = getDataType(18).clone(programDTM);
DataType dt20 = getDataType(20).clone(programDTM);
SwingUtilities.invokeLater(() -> complexUnion.getDataTypeManager().remove(complexUnion,
TaskMonitorAdapter.DUMMY_MONITOR));
DataTypeManager dtm = complexUnion.getDataTypeManager();
Union refUnion = (Union) dtm.getDataType("/testCat/refUnion");
assertNotNull(refUnion);
SwingUtilities.invokeLater(() -> dtm.remove(refUnion, TaskMonitor.DUMMY)); // remove refUnion
waitForSwing();
// refUnion* gets removed since it has only a complexUnion* that was removed.
assertEquals(num - 1, model.getNumComponents());
// refUnion* gets removed (1 component)
num -= 1;
assertEquals(num, model.getNumComponents());
assertTrue(dt18.isEquivalent(getDataType(18)));
assertTrue(dt20.isEquivalent(getDataType(19)));
SwingUtilities.invokeLater(
() -> simpleUnion.getDataTypeManager().remove(simpleUnion, TaskMonitor.DUMMY));
waitForSwing();
// All components (3 total) which were dependent upon simpleUnion are removed
num -= 3;
assertEquals(num, model.getNumComponents());
}
finally {
cleanup();

View file

@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import org.junit.*;
import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datapreview.DataTypePreviewPlugin.DTPPTableModel;
@ -209,12 +210,19 @@ public class DataTypePreviewPluginTest extends AbstractGhidraHeadedIntegrationTe
assertEquals("61004D00200065h", model.getValueAt(4, DTPPTableModel.PREVIEW_COL));// 8-byte long at offset 4
assertEquals("72h", model.getValueAt(5, DTPPTableModel.PREVIEW_COL));// 2-byte short at offset 12
// deactivate program
plugin.getTool().firePluginEvent(new ProgramActivatedPluginEvent("Test", null));
waitForPostedSwingRunnables();
// NOTE: Altering data organization on-the-fly is not supported
dataOrganization.setDefaultAlignment(2);
dataOrganization.setShortSize(3);
dataOrganization.setIntegerSize(3);
dataOrganization.setLongSize(6);
plugin.updateModel();
// activate program
plugin.getTool().firePluginEvent(new ProgramActivatedPluginEvent("Test", program));
waitForPostedSwingRunnables();
gotoService.goTo(addr(program, 0x100df26));

View file

@ -22,7 +22,6 @@ import org.junit.*;
import ghidra.app.cmd.memory.MoveBlockListener;
import ghidra.app.cmd.memory.MoveBlockTask;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.DataTypeManagerDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
@ -81,7 +80,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.initialize(block);
int transactionID = x8051.startTransaction("Set settings");
DataTypeManagerDB dtm = ((ProgramDB) x8051).getDataManager();
DataTypeManagerDB dtm = (DataTypeManagerDB) x8051.getDataTypeManager();
for (int i = 0; i < 10; i++) {
Address a = getAddr(x8051, "BITS", i);
dtm.setStringSettingsValue(a, "color", "red" + i);
@ -197,8 +196,7 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
waitForCondition(() -> moveCompleted && x8051.canLock());
// make sure settings on data got moved
DataTypeManagerDB dtm = ((ProgramDB) x8051).getDataManager();
DataTypeManagerDB dtm = (DataTypeManagerDB) x8051.getDataTypeManager();
for (int i = 0; i < 10; i++) {
Address a = getAddr(x8051, "CODE", 0x2000 + i);

View file

@ -439,7 +439,7 @@ public class FollowFlowProgramBuilder extends ProgramBuilder {
startTransaction();
ProgramDB program = getProgram();
Listing listing = program.getListing();
Structure struct = new StructureDataType(name, thisStructureSize, program.getDataManager());
Structure struct = new StructureDataType(name, thisStructureSize, program.getDataTypeManager());
struct.replaceAtOffset(0, new FloatDataType(), 4, null, null);
struct.replaceAtOffset(pointerOffset, new Pointer32DataType(), 4, null, null);
listing.createData(addr(startOfStruct), struct);
@ -473,7 +473,7 @@ public class FollowFlowProgramBuilder extends ProgramBuilder {
startTransaction();
ProgramDB program = getProgram();
Listing listing = program.getListing();
Structure struct = new StructureDataType(name, thisStructureSize, program.getDataManager());
Structure struct = new StructureDataType(name, thisStructureSize, program.getDataTypeManager());
struct.replaceAtOffset(0, new FloatDataType(), 4, null, null);
struct.replaceAtOffset(pointerOffset, new Pointer32DataType(), 4, null, null);
struct.replaceAtOffset(pointerOffset + pointerSize, new Pointer32DataType(), 4, null, null);

View file

@ -441,13 +441,8 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
// Check that more than 2 non-equivalent data types trigger the dialog to appear.
//
Category secondCategory = rootCategory.createCategory("testCategory2");
dataType = new CustomDataType(secondCategory.getCategoryPath(), crazyName, 2) {
@Override
public DataTypeManager getDataTypeManager() {
return getProgramDataTypeManager(dataTypeManagers);
}
};
dataType = new CustomDataType(secondCategory.getCategoryPath(), crazyName, 2,
getProgramDataTypeManager(dataTypeManagers));
addDataType(secondCategory, dataType);
showDialogWithoutBlocking(tool, dialog);
@ -1099,6 +1094,10 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
}
private class CustomDataType extends StructureDataType {
public CustomDataType(CategoryPath path, String name, int length, DataTypeManager dtm) {
super(path, name, length, dtm);
}
public CustomDataType(CategoryPath path, String name, int length) {
super(path, name, length);
}

View file

@ -55,7 +55,7 @@ public class ArrayTest extends AbstractGhidraHeadedIntegrationTest {
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
listing = program.getListing();
space = program.getAddressFactory().getDefaultAddressSpace();
transactionID = program.startTransaction("Test");

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.program.database.data;
import static org.junit.Assert.assertEquals;
import org.junit.*;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.task.TaskMonitor;
public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationTest {
private ProgramDB program;
private int transactionID;
private Structure struct;
private AddressSpace space;
private TestEnv env;
private CodeBrowserPlugin plugin;
@Before
public void setUp() throws Exception {
program = createDefaultProgram("Test", ProgramBuilder._TOY, this); // big-endian
startTransaction();
space = program.getAddressFactory().getDefaultAddressSpace();
program.getMemory().createInitializedBlock("m", addr(0x1000), 0x100, (byte) 0,
TaskMonitor.DUMMY, false);
struct = createStructure("Test", 0);
struct.setInternallyAligned(true);
struct.addBitField(IntegerDataType.dataType, 3, "bf1", "Nuts");
struct.addBitField(IntegerDataType.dataType, 24, "bf2", null);
struct.addBitField(IntegerDataType.dataType, 4, "bf3", null);
struct.addBitField(IntegerDataType.dataType, 12, "bf4", null);
struct.addBitField(IntegerDataType.dataType, 3, "bf4a", null);
struct.addBitField(IntegerDataType.dataType, 3, "bf5", null);
struct.addBitField(IntegerDataType.dataType, 3, "b6", null);
struct.add(new ByteDataType(), "field0", "Comment1");
struct.add(new WordDataType(), null, "Comment2");
struct.add(new DWordDataType(), "field3", null);
struct.add(new ByteDataType(), "field4", "Comment4");
program.getListing().createData(addr(0x1010), struct);
env = new TestEnv();
PluginTool tool = env.launchDefaultTool(program);
plugin = getPlugin(tool, CodeBrowserPlugin.class);
}
private Address addr(long value) {
return space.getAddress(value);
}
@After
public void tearDown() throws Exception {
endTransaction();
env.dispose();
}
protected Structure createStructure(String name, int length) {
return (Structure) getDataTypeManager().resolve(new StructureDataType(name, length), null);
}
protected DataTypeManager getDataTypeManager() {
return program.getDataTypeManager();
}
private void startTransaction() {
transactionID = program.startTransaction("Test");
}
private void endTransaction() {
program.endTransaction(transactionID, true);
}
@Test
public void testBitField() throws Exception {
openStructure(addr(0x1010));
assertMnemonic("Test", addr(0x1010), 0);
assertMnemonic("int:3", addr(0x1010), 1);
assertMnemonic("int:24", addr(0x1010), 2);
assertMnemonic("int:4", addr(0x1013), 0);
assertMnemonic("int:12", addr(0x1014), 0);
assertMnemonic("int:3", addr(0x1015), 0);
assertMnemonic("int:3", addr(0x1015), 1);
assertMnemonic("int:3", addr(0x1016), 0);
assertMnemonic("db", addr(0x1017), 0);
assertMnemonic("dw", addr(0x1018), 0);
System.out.println("wait");
}
private void assertMnemonic(String expectedValue, Address addr, int occurrence) {
plugin.goToField(addr, "Mnemonic", occurrence, 0, 0);
assertEquals(expectedValue, plugin.getCurrentFieldText());
}
private void openStructure(Address address) {
// open the structure
plugin.goToField(address, "+", 0, 0);
click(plugin, 1);
waitForSwing();
}
}

View file

@ -73,7 +73,7 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
eventRecordingListener = new CategoryTestListener();
dataMgr.addDataTypeManagerListener(eventRecordingListener);
root = dataMgr.getRootCategory();

View file

@ -59,7 +59,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
startTransaction();
}

View file

@ -37,7 +37,7 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
startTransaction();
}

View file

@ -48,7 +48,7 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
public void setUp() throws Exception {
program = createDefaultProgram("Test", ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
transactionID = program.startTransaction("Test");
}

View file

@ -46,7 +46,7 @@ public class FunctionDefinitionDBTest extends AbstractGhidraHeadedIntegrationTes
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dtm = program.getDataManager();
dtm = program.getDataTypeManager();
startTransaction();
FunctionDefinitionDataType fdt = new FunctionDefinitionDataType("test");
fdt.setComment("My comments");

View file

@ -55,7 +55,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
space = program.getAddressFactory().getDefaultAddressSpace();
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
listing = program.getListing();
transactionID = program.startTransaction("Test");
addBlock();

View file

@ -629,7 +629,7 @@ public class FunctionDBTest extends AbstractGhidraHeadedIntegrationTest implemen
int initialParamCnt = f.getParameterCount();
Structure bar = new StructureDataType("bar", 20);
Pointer barPtr = program.getDataManager().getPointer(bar);
Pointer barPtr = program.getDataTypeManager().getPointer(bar);
Parameter returnVar = f.getReturn();
Parameter p1 = f.getParameter(0);
@ -697,7 +697,7 @@ public class FunctionDBTest extends AbstractGhidraHeadedIntegrationTest implemen
int initialParamCnt = f.getParameterCount();
Structure bar = new StructureDataType("bar", 20);
Pointer barPtr = program.getDataManager().getPointer(bar);
Pointer barPtr = program.getDataTypeManager().getPointer(bar);
Parameter returnVar = f.getReturn();
Parameter p1 = f.getParameter(0);

View file

@ -114,6 +114,7 @@ public class DataTypeUtilsTest {
}
private class DataTypeDummy implements DataType {
String wrappedString;
UniversalID id;
@ -132,6 +133,11 @@ public class DataTypeUtilsTest {
return null;
}
@Override
public DataOrganization getDataOrganization() {
throw new UnsupportedOperationException();
}
@Override
public String getDisplayName() {
return "This is a wrapper for: " + wrappedString;

View file

@ -22,6 +22,7 @@ import java.util.List;
import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.cparser.C.ParseException;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
@ -39,6 +40,7 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
private volatile boolean dataChangeCalled;
private Structure bigStruct;
private ProgramDB program;
private DataTypeManagerService service;
private volatile boolean tableRowsChanged;
class MyModelChangeListener implements ModelChangeListener {
@ -61,7 +63,6 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
program = builder.getProgram();
bigStruct = new StructureDataType("bigStruct", 20);
resolveBigStruct();
model = new FunctionEditorModel(null /* use default parser*/, fun);
model.setModelChangeListener(new MyModelChangeListener());
}
@ -73,7 +74,6 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
Function fun = builder.createEmptyFunction("bob", "1000", 20, new VoidDataType());
program = builder.getProgram();
resolveBigStruct();
model = new FunctionEditorModel(null /* use default parser*/, fun);
model.setModelChangeListener(new MyModelChangeListener());
}
@ -81,7 +81,7 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
private void resolveBigStruct() {
int txId = program.startTransaction("Resolve bigStruct");
try {
program.getDataManager().resolve(bigStruct, null);
program.getDataTypeManager().resolve(bigStruct, null);
}
finally {
program.endTransaction(txId, true);
@ -972,9 +972,9 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
assertEquals("Stack[0x4]:1", param.getStorage().toString());
DataType struct = new StructureDataType("bigStruct", 100);
DataType structPtr = PointerDataType.getPointer(struct, program.getDataManager());
DataType structPtr = PointerDataType.getPointer(struct, program.getDataTypeManager());
DataType voidPtr =
PointerDataType.getPointer(VoidDataType.dataType, program.getDataManager());
PointerDataType.getPointer(VoidDataType.dataType, program.getDataTypeManager());
model.setCallingConventionName(CompilerSpec.CALLING_CONVENTION_thiscall);
@ -1081,9 +1081,10 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
assertEquals("CL:1", param.getStorage().toString());
DataType struct = new StructureDataType("bigStruct", 100);
DataType structPtr = PointerDataType.getPointer(struct, program.getDataManager());
DataType structPtr = PointerDataType.getPointer(struct, program.getDataTypeManager());
DataType voidPtr =
PointerDataType.getPointer(VoidDataType.dataType, program.getDataManager());
PointerDataType.getPointer(VoidDataType.dataType, program.getDataTypeManager());
model.setCallingConventionName(CompilerSpec.CALLING_CONVENTION_thiscall);
@ -1722,7 +1723,7 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
int txId = program.startTransaction("Add TypeDef jjjjjj");
try {
DataType dt = new TypedefDataType("jjjjjj", ByteDataType.dataType);
program.getDataManager().resolve(dt, null);
program.getDataTypeManager().resolve(dt, null);
}
finally {
program.endTransaction(txId, true);
@ -1919,4 +1920,5 @@ public class FunctionEditorModelTest extends AbstractGenericTest {
private String getSignatureText() {
return model.getFunctionSignatureTextFromModel();
}
}

View file

@ -63,7 +63,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
startTransaction();
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);

View file

@ -1407,11 +1407,11 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest {
String name = componentAtIndex.getFieldName();
if (optionalName != null) {
destinationComposite.insert(insertIndex, componentCopy, componentCopy.getLength(),
destinationComposite.insert(insertIndex, componentCopy, componentAtIndex.getLength(),
optionalName, null);
}
else {
destinationComposite.insert(insertIndex, componentCopy, componentCopy.getLength(),
destinationComposite.insert(insertIndex, componentCopy, componentAtIndex.getLength(),
name + " Copy", null);
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
public class MSVCStructureDBBitFieldTest extends MSVCStructureImplBitFieldTest {
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
getDataTypeManager().startTransaction("Test");
super.setUp();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCStructureDBBitFieldTest.class) {
if (dataMgr == null) {
dataMgr = new StandAloneDataTypeManager("Test");
DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization();
DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg);
}
return dataMgr;
}
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
public class MSVCUnionDBBitFieldTest extends MSVCUnionImplBitFieldTest {
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
getDataTypeManager().startTransaction("Test");
super.setUp();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCUnionDBBitFieldTest.class) {
if (dataMgr == null) {
dataMgr = new StandAloneDataTypeManager("Test");
DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization();
DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg);
}
return dataMgr;
}
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
public class StructureDBBigEndianBitFieldTest extends StructureImplBigEndianBitFieldTest {
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
getDataTypeManager().startTransaction("Test");
super.setUp();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCStructureDBBitFieldTest.class) {
if (dataMgr == null) {
dataMgr = new StandAloneDataTypeManager("Test");
DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization();
DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg);
}
return dataMgr;
}
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
public class StructureDBLittleEndianBitFieldTest extends StructureImplLittleEndianBitFieldTest {
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
getDataTypeManager().startTransaction("Test");
super.setUp();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCStructureDBBitFieldTest.class) {
if (dataMgr == null) {
dataMgr = new StandAloneDataTypeManager("Test");
DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization();
DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg);
}
return dataMgr;
}
}
}

View file

@ -22,21 +22,30 @@ import static org.junit.Assert.*;
import org.junit.*;
import generic.test.AbstractGenericTest;
import generic.test.AbstractGTest;
import ghidra.program.model.data.*;
/**
*
*/
public class StructureImplTest extends AbstractGenericTest {
public class StructureDataTypeTest extends AbstractGTest {
private Structure struct;
/**
* @param arg0
*/
public StructureImplTest() {
super();
@Before
public void setUp() throws Exception {
struct = createStructure("TestStruct", 0);
struct.add(new ByteDataType(), "field1", "Comment1");
struct.add(new WordDataType(), null, "Comment2");
struct.add(new DWordDataType(), "field3", null);
struct.add(new ByteDataType(), "field4", "Comment4");
}
private void transitionToBigEndian() {
// transition default little-endian structure to big-endian
DataTypeManager beDtm = new MyBigEndianDataTypeManager();
struct = (Structure) struct.clone(beDtm);
}
private Structure createStructure(String name, int length) {
@ -56,21 +65,7 @@ public class StructureImplTest extends AbstractGenericTest {
}
private Pointer createPointer(DataType dataType, int length) {
return new Pointer32DataType(dataType);
}
@Before
public void setUp() throws Exception {
struct = createStructure("Test", 0);
struct.add(new ByteDataType(), "field0", "Comment1");
struct.add(new WordDataType(), null, "Comment2");
struct.add(new DWordDataType(), "field3", null);
struct.add(new ByteDataType(), "field4", "Comment4");
}
@After
public void tearDown() throws Exception {
return new PointerDataType(dataType, length);
}
@Test
@ -81,14 +76,15 @@ public class StructureImplTest extends AbstractGenericTest {
DataTypeComponent dtc = struct.getComponent(0);
assertEquals(0, dtc.getOffset());
assertEquals(0, dtc.getOrdinal());
assertEquals("field0", dtc.getFieldName());
assertEquals("field1", dtc.getFieldName());
assertEquals("Comment1", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(1);
assertEquals(1, dtc.getOffset());
assertEquals(1, dtc.getOrdinal());
assertNull(dtc.getFieldName());
assertEquals("field_0x1", dtc.getDefaultFieldName());
assertEquals(null, dtc.getFieldName());
assertEquals("Comment2", dtc.getComment());
assertEquals(WordDataType.class, dtc.getDataType().getClass());
@ -115,8 +111,8 @@ public class StructureImplTest extends AbstractGenericTest {
assertEquals(10, struct.getLength());
assertEquals(10, struct.getNumComponents());
struct.add(new ByteDataType(), "field0", "Comment1");
struct.add(new WordDataType(), "field1", "Comment2");
struct.add(new ByteDataType(), "field1", "Comment1");
struct.add(new WordDataType(), null, "Comment2");
struct.add(new DWordDataType(), "field3", null);
struct.add(new ByteDataType(), "field4", "Comment4");
@ -126,28 +122,33 @@ public class StructureImplTest extends AbstractGenericTest {
DataTypeComponent dtc = struct.getComponent(0);
assertEquals(0, dtc.getOffset());
assertEquals(0, dtc.getOrdinal());
assertEquals("field_0x0", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals(null, dtc.getComment());
assertNull(dtc.getComment());
assertEquals(DataType.DEFAULT, dtc.getDataType());
dtc = struct.getComponent(1);
assertEquals(1, dtc.getOffset());
assertEquals(1, dtc.getOrdinal());
assertEquals("field_0x1", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals(null, dtc.getComment());
assertEquals(DataType.DEFAULT, dtc.getDataType());
dtc = struct.getComponent(10);
assertEquals(10, dtc.getOffset());
assertEquals(10, dtc.getOrdinal());
assertEquals("field0", dtc.getFieldName());
assertEquals("field1", dtc.getFieldName());
assertEquals("Comment1", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(11);
assertEquals(11, dtc.getOffset());
assertEquals(11, dtc.getOrdinal());
assertEquals("field1", dtc.getFieldName());
assertEquals("field_0xb", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals("Comment2", dtc.getComment());
assertEquals(WordDataType.class, dtc.getDataType().getClass());
@ -168,20 +169,22 @@ public class StructureImplTest extends AbstractGenericTest {
DataTypeComponent dtc = struct.getComponent(0);
assertEquals(0, dtc.getOffset());
assertEquals(0, dtc.getOrdinal());
assertEquals(null, dtc.getFieldName());
assertEquals(null, dtc.getComment());
assertEquals("field_0x0", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertNull(dtc.getComment());
assertEquals(FloatDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(1);
assertEquals(4, dtc.getOffset());
assertEquals(1, dtc.getOrdinal());
assertEquals("field0", dtc.getFieldName());
assertEquals("field1", dtc.getFieldName());
assertEquals("Comment1", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(2);
assertEquals(5, dtc.getOffset());
assertEquals(2, dtc.getOrdinal());
assertEquals("field_0x5", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals("Comment2", dtc.getComment());
assertEquals(WordDataType.class, dtc.getDataType().getClass());
@ -204,6 +207,7 @@ public class StructureImplTest extends AbstractGenericTest {
@Test
public void testInsert_end() {
struct.insert(4, new FloatDataType());
assertEquals(12, struct.getLength());
assertEquals(5, struct.getNumComponents());
@ -211,14 +215,15 @@ public class StructureImplTest extends AbstractGenericTest {
DataTypeComponent dtc = struct.getComponent(0);
assertEquals(0, dtc.getOffset());
assertEquals(0, dtc.getOrdinal());
assertEquals("field0", dtc.getFieldName());
assertEquals("field1", dtc.getFieldName());
assertEquals("Comment1", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(1);
assertEquals(1, dtc.getOffset());
assertEquals(1, dtc.getOrdinal());
assertEquals(null, dtc.getFieldName());
assertEquals("field_0x1", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals("Comment2", dtc.getComment());
assertEquals(WordDataType.class, dtc.getDataType().getClass());
@ -239,6 +244,7 @@ public class StructureImplTest extends AbstractGenericTest {
dtc = struct.getComponent(4);
assertEquals(8, dtc.getOffset());
assertEquals(4, dtc.getOrdinal());
assertEquals("field_0x8", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals(null, dtc.getComment());
assertEquals(FloatDataType.class, dtc.getDataType().getClass());
@ -247,6 +253,7 @@ public class StructureImplTest extends AbstractGenericTest {
@Test
public void testInsert_middle() {
struct.insert(2, new FloatDataType());
assertEquals(12, struct.getLength());
assertEquals(5, struct.getNumComponents());
@ -254,13 +261,14 @@ public class StructureImplTest extends AbstractGenericTest {
DataTypeComponent dtc = struct.getComponent(0);
assertEquals(0, dtc.getOffset());
assertEquals(0, dtc.getOrdinal());
assertEquals("field0", dtc.getFieldName());
assertEquals("field1", dtc.getFieldName());
assertEquals("Comment1", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(1);
assertEquals(1, dtc.getOffset());
assertEquals(1, dtc.getOrdinal());
assertEquals("field_0x1", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals("Comment2", dtc.getComment());
assertEquals(WordDataType.class, dtc.getDataType().getClass());
@ -268,8 +276,9 @@ public class StructureImplTest extends AbstractGenericTest {
dtc = struct.getComponent(2);
assertEquals(3, dtc.getOffset());
assertEquals(2, dtc.getOrdinal());
assertEquals("field_0x3", dtc.getDefaultFieldName());
assertNull(dtc.getFieldName());
assertEquals(null, dtc.getComment());
assertNull(dtc.getComment());
assertEquals(FloatDataType.class, dtc.getDataType().getClass());
dtc = struct.getComponent(3);
@ -285,6 +294,7 @@ public class StructureImplTest extends AbstractGenericTest {
assertEquals("field4", dtc.getFieldName());
assertEquals("Comment4", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
}
@Test
@ -403,6 +413,304 @@ public class StructureImplTest extends AbstractGenericTest {
assertEquals(104, struct.getLength());
}
@Test
public void testInsertBitFieldLittleEndianAppend() throws Exception {
struct.insertBitField(4, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
" 3 dword 4 field3 \"\"\n" +
" 7 byte 1 field4 \"Comment4\"\n" +
" 8 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 9 undefined 1 null \"\"\n" +
" 10 undefined 1 null \"\"\n" +
" 11 undefined 1 null \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitField(4, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
" 3 dword 4 field3 \"\"\n" +
" 7 byte 1 field4 \"Comment4\"\n" +
" 8 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 8 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 9 undefined 1 null \"\"\n" +
" 10 undefined 1 null \"\"\n" +
" 11 undefined 1 null \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testInsertBitFieldAtLittleEndianAppend() throws Exception {
struct.insertBitFieldAt(10, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
" 3 dword 4 field3 \"\"\n" +
" 7 byte 1 field4 \"Comment4\"\n" +
" 8 undefined 1 null \"\"\n" +
" 9 undefined 1 null \"\"\n" +
" 10 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 11 undefined 1 null \"\"\n" +
" 12 undefined 1 null \"\"\n" +
" 13 undefined 1 null \"\"\n" +
"}\n" +
"Size = 14 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitFieldAt(10, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
" 3 dword 4 field3 \"\"\n" +
" 7 byte 1 field4 \"Comment4\"\n" +
" 8 undefined 1 null \"\"\n" +
" 9 undefined 1 null \"\"\n" +
" 10 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 10 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 11 undefined 1 null \"\"\n" +
" 12 undefined 1 null \"\"\n" +
" 13 undefined 1 null \"\"\n" +
"}\n" +
"Size = 14 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testInsertBitFieldAtLittleEndian() throws Exception {
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 3 undefined 1 null \"\"\n" +
" 4 undefined 1 null \"\"\n" +
" 5 undefined 1 null \"\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 3 undefined 1 null \"\"\n" +
" 4 undefined 1 null \"\"\n" +
" 5 undefined 1 null \"\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 5 undefined 1 null \"\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
try {
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 12, "bf4", "bf4Comment");
fail(
"expected - IllegalArgumentException: Bitfield does not fit within specified constraints");
}
catch (IllegalArgumentException e) {
// expected
}
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testInsertBitFieldAtBigEndian() throws Exception {
transitionToBigEndian();
try {
struct.insertBitFieldAt(2, 4, 30, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
fail(
"expected - IllegalArgumentException: Bitfield does not fit within specified constraints");
}
catch (IllegalArgumentException e) {
// expected
}
struct.insertBitFieldAt(2, 4, 29, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(5) 1 bf1 \"bf1Comment\"\n" +
" 3 undefined 1 null \"\"\n" +
" 4 undefined 1 null \"\"\n" +
" 5 undefined 1 null \"\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 26, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(5) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(2) 1 bf2 \"bf2Comment\"\n" +
" 3 undefined 1 null \"\"\n" +
" 4 undefined 1 null \"\"\n" +
" 5 undefined 1 null \"\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 11, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(5) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(2) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(3) 3 bf3 \"bf3Comment\"\n" +
" 5 undefined 1 null \"\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 11, "bf4", "bf4Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 int:3(5) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(2) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(3) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(0) 2 bf4 \"bf4Comment\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testInsertAtOffsetAfterBeforeBitField() throws Exception {
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment");
struct.insertAtOffset(2, FloatDataType.dataType, 4);
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 undefined 1 null \"\"\n" +
" 2 float 4 null \"\"\n" +
" 6 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 6 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 6 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 8 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 10 word 2 null \"Comment2\"\n" +
" 12 dword 4 field3 \"\"\n" +
" 16 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 17 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testClearComponent() {
struct.clearComponent(0);
@ -444,7 +752,17 @@ public class StructureImplTest extends AbstractGenericTest {
}
@Test
public void testReplace1() {// bigger, space below
public void testReplace() { // bigger, no space below
try {
struct.replace(0, new QWordDataType(), 8);
Assert.fail();
}
catch (Exception e) {
}
}
@Test
public void testReplace1() { // bigger, space below
struct.insert(1, new QWordDataType());
struct.clearComponent(1);
assertEquals(16, struct.getLength());
@ -459,7 +777,7 @@ public class StructureImplTest extends AbstractGenericTest {
}
@Test
public void testReplace2() {// same size
public void testReplace2() { // same size
struct.replace(0, new CharDataType(), 1);
assertEquals(8, struct.getLength());
assertEquals(4, struct.getNumComponents());
@ -470,7 +788,7 @@ public class StructureImplTest extends AbstractGenericTest {
}
@Test
public void testReplace3() {// smaller
public void testReplace3() { // smaller
struct.replace(1, new CharDataType(), 1);
assertEquals(8, struct.getLength());
assertEquals(5, struct.getNumComponents());
@ -623,13 +941,66 @@ public class StructureImplTest extends AbstractGenericTest {
s.add(new FloatDataType());
struct.add(s);
s.deleteAll();
assertEquals(1, s.getLength());
assertTrue(s.isNotYetDefined());
assertEquals(0, s.getNumComponents());
}
@Test
public void testGetComponents() {
struct = createStructure("Test", 8);
struct.insert(2, new ByteDataType(), 1, "field3", "Comment1");
struct.insert(5, new WordDataType(), 2, null, "Comment2");
struct.insert(7, new DWordDataType(), 4, "field8", null);
assertEquals(15, struct.getLength());
assertEquals(11, struct.getNumComponents());
DataTypeComponent[] dtcs = struct.getComponents();
assertEquals(11, dtcs.length);
int offset = 0;
for (int i = 0; i < 11; i++) {
assertEquals(i, dtcs[i].getOrdinal());
assertEquals(offset, dtcs[i].getOffset());
offset += dtcs[i].getLength();
}
assertEquals(DataType.DEFAULT, dtcs[0].getDataType());
assertEquals(DataType.DEFAULT, dtcs[1].getDataType());
assertEquals(ByteDataType.class, dtcs[2].getDataType().getClass());
assertEquals(DataType.DEFAULT, dtcs[3].getDataType());
assertEquals(DataType.DEFAULT, dtcs[4].getDataType());
assertEquals(WordDataType.class, dtcs[5].getDataType().getClass());
assertEquals(DataType.DEFAULT, dtcs[6].getDataType());
assertEquals(DWordDataType.class, dtcs[7].getDataType().getClass());
assertEquals(DataType.DEFAULT, dtcs[8].getDataType());
assertEquals(DataType.DEFAULT, dtcs[9].getDataType());
assertEquals(DataType.DEFAULT, dtcs[10].getDataType());
}
@Test
public void testGetDefinedComponents() {
struct = createStructure("Test", 8);
struct.insert(2, new ByteDataType(), 1, "field3", "Comment1");
struct.insert(5, new WordDataType(), 2, null, "Comment2");
struct.insert(7, new DWordDataType(), 4, "field8", null);
assertEquals(15, struct.getLength());
assertEquals(11, struct.getNumComponents());
DataTypeComponent[] dtcs = struct.getDefinedComponents();
assertEquals(3, dtcs.length);
assertEquals(ByteDataType.class, dtcs[0].getDataType().getClass());
assertEquals(2, dtcs[0].getOrdinal());
assertEquals(2, dtcs[0].getOffset());
assertEquals(WordDataType.class, dtcs[1].getDataType().getClass());
assertEquals(5, dtcs[1].getOrdinal());
assertEquals(5, dtcs[1].getOffset());
assertEquals(DWordDataType.class, dtcs[2].getDataType().getClass());
assertEquals(7, dtcs[2].getOrdinal());
assertEquals(8, dtcs[2].getOffset());
}
@Test
public void testGetComponentAt() {
DataTypeComponent dtc = struct.getComponentAt(4);
@ -638,19 +1009,6 @@ public class StructureImplTest extends AbstractGenericTest {
assertEquals(3, dtc.getOffset());
}
@Test
public void testGetDataTypeAt() {
Structure s1 = createStructure("Test1", 0);
s1.add(new WordDataType());
s1.add(struct);
s1.add(new ByteDataType());
DataTypeComponent dtc = s1.getComponentAt(7);
assertEquals(struct, dtc.getDataType());
dtc = s1.getDataTypeAt(7);
assertEquals(DWordDataType.class, dtc.getDataType().getClass());
}
@Test
public void testAddVarLengthDataTypes() {
Structure s1 = createStructure("Test1", 0);
@ -732,6 +1090,134 @@ public class StructureImplTest extends AbstractGenericTest {
assertEquals("NewTypedef", typdef.getName());
}
@Test
public void testGetDataTypeAt() {
Structure s1 = createStructure("Test1", 0);
s1.add(new WordDataType());
s1.add(struct);
s1.add(new ByteDataType());
DataTypeComponent dtc = s1.getComponentAt(7);
assertEquals(struct, dtc.getDataType());
dtc = s1.getDataTypeAt(7);
assertEquals(DWordDataType.class, dtc.getDataType().getClass());
}
@Test
public void testReplaceWith() {
assertEquals(8, struct.getLength());
assertEquals(4, struct.getNumComponents());
Structure newStruct = createStructure("Replaced", 8);
newStruct.setDescription("testReplaceWith()");
DataTypeComponent dtc0 = newStruct.insert(2, new ByteDataType(), 1, "field3", "Comment1");
DataTypeComponent dtc1 = newStruct.insert(5, new WordDataType(), 2, null, "Comment2");
DataTypeComponent dtc2 = newStruct.insert(7, new DWordDataType(), 4, "field8", null);
struct.replaceWith(newStruct);
assertEquals(15, struct.getLength());
assertEquals(11, struct.getNumComponents());
DataTypeComponent[] dtcs = struct.getDefinedComponents();
assertEquals(3, dtcs.length);
assertEquals(dtc0, dtcs[0]);
assertEquals(dtc1, dtcs[1]);
assertEquals(dtc2, dtcs[2]);
assertEquals("TestStruct", struct.getName());
assertEquals("", struct.getDescription());
}
@Test
public void testReplaceWith2() throws InvalidDataTypeException {
// NOTE: unaligned bitfields should remain unchanged when
// transitioning endianess even though it makes little sense.
// Unaligned structures are not intended to be portable!
TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType);
struct.insertBitFieldAt(9, 1, 0, td, 4, "MyBit1", "bitComment1");
struct.insertBitFieldAt(9, 1, 4, td, 3, "MyBit2", "bitComment2");
struct.insertBitFieldAt(9, 2, 7, td, 2, "MyBit3", "bitComment3");
struct.growStructure(1);
struct.setFlexibleArrayComponent(td, "myFlex", "flexComment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"Unaligned\n" +
"Structure TestStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
" 3 dword 4 field3 \"\"\n" +
" 7 byte 1 field4 \"Comment4\"\n" +
" 8 undefined 1 null \"\"\n" +
" 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" +
" 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" +
" 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" +
" 11 undefined 1 null \"\"\n" +
" Foo[0] 0 myFlex \"flexComment\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 1", struct);
//@formatter:on
DataTypeManager beDtm = new MyBigEndianDataTypeManager();
Structure newStruct = new StructureDataType("bigStruct", 0, beDtm);
newStruct.replaceWith(struct);
assertTrue(newStruct.getDataOrganization().isBigEndian());
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/bigStruct\n" +
"Unaligned\n" +
"Structure bigStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
" 3 dword 4 field3 \"\"\n" +
" 7 byte 1 field4 \"Comment4\"\n" +
" 8 undefined 1 null \"\"\n" +
" 9 Foo:4(0) 1 MyBit1 \"bitComment1\"\n" +
" 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" +
" 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" +
" 11 undefined 1 null \"\"\n" +
" Foo[0] 0 myFlex \"flexComment\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 1", newStruct);
//@formatter:on
}
/**
* Test that a structure can't ... ???
*/
@Test
public void testCyclingProblem() {
Structure newStruct = createStructure("TestStruct", 80);
newStruct.setDescription("testReplaceWith()");
newStruct.add(new ByteDataType(), "field0", "Comment1");
newStruct.add(struct, "field1", null);
newStruct.add(new WordDataType(), null, "Comment2");
newStruct.add(new DWordDataType(), "field3", null);
try {
struct.add(newStruct);
Assert.fail();
}
catch (IllegalArgumentException e) {
}
try {
struct.insert(0, newStruct);
Assert.fail();
}
catch (IllegalArgumentException e) {
}
try {
struct.replace(0, newStruct, newStruct.getLength());
Assert.fail();
}
catch (IllegalArgumentException e) {
}
}
/**
* Test that a structure can't be added to itself.
*/
@ -1049,4 +1535,13 @@ public class StructureImplTest extends AbstractGenericTest {
}
}
protected class MyBigEndianDataTypeManager extends StandAloneDataTypeManager {
MyBigEndianDataTypeManager() {
super("BEdtm");
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
dataOrg.setBigEndian(true);
this.dataOrganization = dataOrg;
}
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
public class UnionDBBigEndianBitFieldTest extends UnionImplBigEndianBitFieldTest {
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
getDataTypeManager().startTransaction("Test");
super.setUp();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCStructureDBBitFieldTest.class) {
if (dataMgr == null) {
dataMgr = new StandAloneDataTypeManager("Test");
DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization();
DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg);
}
return dataMgr;
}
}
}

View file

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
public class UnionDBLittleEndianBitFieldTest extends UnionImplLittleEndianBitFieldTest {
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
getDataTypeManager().startTransaction("Test");
super.setUp();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCStructureDBBitFieldTest.class) {
if (dataMgr == null) {
dataMgr = new StandAloneDataTypeManager("Test");
DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization();
DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg);
}
return dataMgr;
}
}
}

View file

@ -13,79 +13,59 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
*
*/
package ghidra.program.database.data;
import static org.junit.Assert.*;
import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import generic.test.AbstractGTest;
import ghidra.program.model.data.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
/**
* Test the database implementation of Union data type.
*
*
*
*/
public class UnionTest extends AbstractGenericTest {
public class UnionDataTypeTest extends AbstractGTest {
private Union union;
private ProgramDB program;
private DataTypeManagerDB dataMgr;
private int transactionID;
/**
* Constructor for UnionTest.
* @param name
*/
public UnionTest() {
super();
}
private Structure createStructure(String name, int length) {
return (Structure) dataMgr.resolve(new StructureDataType(name, length),
DataTypeConflictHandler.DEFAULT_HANDLER);
}
private Union createUnion(String name) {
return (Union) dataMgr.resolve(new UnionDataType(name),
DataTypeConflictHandler.DEFAULT_HANDLER);
}
private TypeDef createTypeDef(DataType dataType) {
return (TypeDef) dataMgr.resolve(
new TypedefDataType(dataType.getName() + "TypeDef", dataType), null);
}
private Array createArray(DataType dataType, int numElements) {
return (Array) dataMgr.resolve(
new ArrayDataType(dataType, numElements, dataType.getLength()), null);
}
private Pointer createPointer(DataType dataType, int length) {
return (Pointer) dataMgr.resolve(new Pointer32DataType(dataType), null);
}
@Before
public void setUp() throws Exception {
program =
AbstractGhidraHeadlessIntegrationTest.createDefaultProgram("Test", ProgramBuilder._TOY, this);
dataMgr = program.getDataManager();
transactionID = program.startTransaction("Test");
union = createUnion("Test");
union = createUnion("TestUnion");
union.add(new ByteDataType(), "field1", "Comment1");
union.add(new WordDataType(), null, "Comment2");
union.add(new DWordDataType(), "field3", null);
union.add(new ByteDataType(), "field4", "Comment4");
}
@After
public void tearDown() throws Exception {
program.endTransaction(transactionID, true);
program.release(this);
private void transitionToBigEndian() {
// transition default little-endian structure to big-endian
DataTypeManager beDtm = new MyBigEndianDataTypeManager();
union = (Union) union.clone(beDtm);
}
private Union createUnion(String name) {
return new UnionDataType(name);
}
private Structure createStructure(String name, int size) {
return new StructureDataType(name, size);
}
private TypeDef createTypeDef(DataType dataType) {
return new TypedefDataType(dataType.getName() + "TypeDef", dataType);
}
private Array createArray(DataType dataType, int numElements) {
return new ArrayDataType(dataType, numElements, dataType.getLength());
}
private Pointer createPointer(DataType dataType, int length) {
return new PointerDataType(dataType, length);
}
@Test
@ -109,12 +89,11 @@ public class UnionTest extends AbstractGenericTest {
@Test
public void testAdd2() {
Structure struct = new StructureDataType("struct_1", 0);
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
union.add(struct);
union.delete(4);
assertEquals(4, union.getNumComponents());
assertEquals(4, union.getLength());
@ -122,7 +101,7 @@ public class UnionTest extends AbstractGenericTest {
@Test
public void testGetComponent() {
Structure struct = new StructureDataType("struct_1", 0);
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
DataTypeComponent newdtc = union.add(struct, "field5", "comments");
@ -135,7 +114,7 @@ public class UnionTest extends AbstractGenericTest {
@Test
public void testGetComponents() {
Structure struct = new StructureDataType("struct_1", 0);
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
union.add(struct, "field5", "comments");
@ -147,7 +126,7 @@ public class UnionTest extends AbstractGenericTest {
@Test
public void testInsert() {
Structure struct = new StructureDataType("struct_1", 0);
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
@ -161,20 +140,92 @@ public class UnionTest extends AbstractGenericTest {
}
@Test
public void testGetName() {
assertEquals("Test", union.getName());
public void testBitFieldUnionLength() throws Exception {
int cnt = union.getNumComponents();
for (int i = 0; i < cnt; i++) {
union.delete(0);
}
union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment");
union.insert(0, ShortDataType.dataType);
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"Union TestUnion {\n" +
" 0 short 2 null \"\"\n" +
" 1 byte:2(4) 1 bf1 \"bf1Comment\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", union);
//@formatter:on
}
@Test
public void testClone() throws Exception {
public void testInsertBitFieldLittleEndian() throws Exception {
union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
" 0 int:4(0) 1 bf1 \"bf1Comment\"\n" +
" 0 byte:4(0) 1 bf2 \"bf2Comment\"\n" +
" 0 dword 4 field3 \"\"\n" +
" 0 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", union);
//@formatter:on
}
@Test
public void testInsertBitFieldBigEndian() throws Exception {
transitionToBigEndian();
union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
" 0 int:4(4) 1 bf1 \"bf1Comment\"\n" +
" 0 byte:4(4) 1 bf2 \"bf2Comment\"\n" +
" 0 dword 4 field3 \"\"\n" +
" 0 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", union);
//@formatter:on
}
@Test
public void testGetName() {
assertEquals("TestUnion", union.getName());
}
@Test
public void testCloneRetainIdentity() throws Exception {
Union unionCopy = (Union) union.clone(null);
assertNull(unionCopy.getDataTypeManager());
assertEquals(4, union.getLength());
}
@Test
public void testCopyDontRetain() throws Exception {
Union unionCopy = (Union) union.copy(null);
assertNull(unionCopy.getDataTypeManager());
assertEquals(4, union.getLength());
}
@Test
public void testDelete() throws Exception {
Structure struct = new StructureDataType("struct_1", 0);
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
union.add(struct);
@ -189,16 +240,15 @@ public class UnionTest extends AbstractGenericTest {
@Test
public void testIsPartOf() {
Structure struct = new StructureDataType("struct_1", 0);
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
DataTypeComponent dtc = struct.add(new StringDataType(), 10);
DataTypeComponent newdtc = union.add(struct);
dtc = union.getComponent(4);
DataTypeComponent dtc = struct.add(createStructure("mystring", 10));
DataType dt = dtc.getDataType();
DataTypeComponent newdtc = union.add(struct);
assertTrue(union.isPartOf(dt));
Structure newstruct = (Structure) newdtc.getDataType();
Structure s1 = (Structure) newstruct.add(new StructureDataType("s1", 1)).getDataType();
Structure s1 = (Structure) newstruct.add(createStructure("s1", 1)).getDataType();
dt = s1.add(new QWordDataType()).getDataType();
assertTrue(union.isPartOf(dt));
@ -206,18 +256,48 @@ public class UnionTest extends AbstractGenericTest {
@Test
public void testReplaceWith() {
Structure struct = new StructureDataType("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
Union newunion = new UnionDataType("newunion");
newunion.add(struct);
assertEquals(4, union.getLength());
assertEquals(4, union.getNumComponents());
union.replaceWith(newunion);
assertEquals(1, newunion.getNumComponents());
DataType dt = dataMgr.getDataType("/struct_1");
assertNotNull(dt);
Union newUnion = createUnion("Replaced");
newUnion.setDescription("testReplaceWith()");
DataTypeComponent dtc2 = newUnion.insert(0, new DWordDataType(), 4, "field2", null);
DataTypeComponent dtc1 = newUnion.insert(0, new WordDataType(), 2, null, "Comment2");
DataTypeComponent dtc0 = newUnion.insert(0, new ByteDataType(), 1, "field0", "Comment1");
assertEquals(dt, union.getComponent(0).getDataType());
union.replaceWith(newUnion);
assertEquals(4, union.getLength());
assertEquals(3, union.getNumComponents());
DataTypeComponent[] dtcs = union.getComponents();
assertEquals(3, dtcs.length);
assertEquals(dtc0, dtcs[0]);
assertEquals(dtc1, dtcs[1]);
assertEquals(dtc2, dtcs[2]);
assertEquals("TestUnion", union.getName());
assertEquals("", union.getDescription()); // unchanged
}
@Test
public void testCyclingProblem() {
Union newUnion = createUnion("Test");
newUnion.setDescription("testReplaceWith()");
newUnion.add(new ByteDataType(), "field0", "Comment1");
newUnion.add(union, "field1", null);
newUnion.add(new WordDataType(), null, "Comment2");
newUnion.add(new DWordDataType(), "field3", null);
try {
union.add(newUnion);
Assert.fail();
}
catch (IllegalArgumentException e) {
}
try {
union.insert(0, newUnion);
Assert.fail();
}
catch (IllegalArgumentException e) {
}
}
/**
@ -453,4 +533,12 @@ public class UnionTest extends AbstractGenericTest {
}
}
protected class MyBigEndianDataTypeManager extends StandAloneDataTypeManager {
MyBigEndianDataTypeManager() {
super("BEdtm");
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
dataOrg.setBigEndian(true);
this.dataOrganization = dataOrg;
}
}
}

View file

@ -0,0 +1,77 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import static org.junit.Assert.assertTrue;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.junit.Before;
import generic.test.AbstractGTest;
import ghidra.app.util.cparser.C.CParser;
import ghidra.util.Msg;
import resources.ResourceManager;
public abstract class AbstractCompositeImplBitFieldTest extends AbstractGTest {
protected static final String C_SOURCE_FILE = "ghidra/app/util/cparser/bitfields.h";
@Before
public void setUp() throws Exception {
DataTypeManager dataMgr = getDataTypeManager();
if (dataMgr.getDataTypeCount(false) != 0) {
Msg.info(this, "Using previously parsed data types");
return; // already have types
}
Msg.info(this, "Parsing data types from " + C_SOURCE_FILE);
CParser parser = new CParser(dataMgr, true, null);
try (InputStream is = ResourceManager.getResourceAsStream(C_SOURCE_FILE)) {
if (is == null) {
throw new FileNotFoundException("Resource not found: " + C_SOURCE_FILE);
}
Msg.debug(this, "Parsing C headers from " + C_SOURCE_FILE);
parser.parse(is);
}
}
protected class MyDataTypeManager extends StandAloneDataTypeManager {
MyDataTypeManager(String name, DataOrganization dataOrg) {
super(name);
this.dataOrganization = dataOrg;
}
}
abstract DataTypeManager getDataTypeManager();
Structure getStructure(String name) {
DataType dataType = getDataTypeManager().getDataType("/" + name);
assertTrue("Data type not found: " + name, dataType instanceof Structure);
return (Structure) dataType;
}
Union getUnion(String name) {
DataType dataType = getDataTypeManager().getDataType("/" + name);
assertTrue("Data type not found: " + name, dataType instanceof Union);
return (Union) dataType;
}
}

View file

@ -0,0 +1,753 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import org.junit.Test;
public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitFieldTest {
private static DataTypeManager dataMgr;
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCStructureImplBitFieldTest.class) {
if (dataMgr == null) {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg);
dataMgr = new MyDataTypeManager("test", dataOrg);
}
return dataMgr;
}
}
@Test
public void testStructureBitFieldsB1() {
Structure struct = getStructure("B1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1\n" +
"Aligned\n" +
"Structure B1 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2() {
Structure struct = getStructure("B2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2\n" +
"Aligned\n" +
"Structure B2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 5 int:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3() {
Structure struct = getStructure("B3");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3\n" +
"Aligned\n" +
"Structure B3 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1() {
Structure struct = getStructure("Z1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" +
"Aligned\n" +
"Structure Z1 {\n" +
" 0 char 1 a \"\"\n" +
" 2 int:0(0) 1 \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2() {
Structure struct = getStructure("Z2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" +
"Aligned\n" +
"Structure Z2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 int:0(0) 1 \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3() {
Structure struct = getStructure("Z3");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" +
"Aligned\n" +
"Structure Z3 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 5 int:4(0) 1 d \"\"\n" +
" 7 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4() {
Structure struct = getStructure("Z4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" +
"Aligned\n" +
"Structure Z4 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5() {
Structure struct = getStructure("Z5");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" +
"Aligned\n" +
"Structure Z5 {\n" +
" 0 char 1 a \"\"\n" +
" 8 int:0(0) 1 \"\"\n" +
" 8 longlong:6(0) 1 b \"\"\n" +
" 16 int:8(0) 1 c \"\"\n" +
" 20 char 1 d \"\"\n" +
"}\n" +
"Size = 24 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ6() {
Structure struct = getStructure("Z6");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" +
"Aligned\n" +
"Structure Z6 {\n" +
" 0 char 1 a \"\"\n" +
" 8 int:0(0) 1 \"\"\n" +
" 8 longlong:6(0) 1 b \"\"\n" +
" 16 int:8(0) 1 c \"\"\n" +
" 20 char 1 d \"\"\n" +
" 24 longlong:6(0) 1 e \"\"\n" +
" 32 int:8(0) 1 f \"\"\n" +
" 36 char 1 g \"\"\n" +
"}\n" +
"Size = 40 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB1p1() {
Structure struct = getStructure("B1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" +
"Aligned pack(1)\n" +
"Structure B1p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 7 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2p1() {
Structure struct = getStructure("B2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" +
"Aligned pack(1)\n" +
"Structure B2p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 4 int:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 7 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3p1() {
Structure struct = getStructure("B3p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" +
"Aligned pack(1)\n" +
"Structure B3p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 7 char 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p1() {
Structure struct = getStructure("Z1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" +
"Aligned pack(1)\n" +
"Structure Z1p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:0(0) 1 \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 7 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2p1() {
Structure struct = getStructure("Z2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" +
"Aligned pack(1)\n" +
"Structure Z2p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 7 int:0(0) 1 \"\"\n" +
" 7 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p1() {
Structure struct = getStructure("Z3p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" +
"Aligned pack(1)\n" +
"Structure Z3p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 4 int:4(0) 1 d \"\"\n" +
" 6 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 7 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p1T() {
Structure struct = getStructure("Z3p1T");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" +
"Aligned\n" +
"Structure Z3p1T {\n" +
" 0 char 1 a \"\"\n" +
" 1 Z3p1 7 z3p1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4p1() {
Structure struct = getStructure("Z4p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" +
"Aligned pack(1)\n" +
"Structure Z4p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 3 int:8(0) 1 c \"\"\n" +
" 7 longlong:0(0) 1 \"\"\n" +
" 7 char 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB1p2() {
Structure struct = getStructure("B1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" +
"Aligned pack(2)\n" +
"Structure B1p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2p2() {
Structure struct = getStructure("B2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" +
"Aligned pack(2)\n" +
"Structure B2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 5 int:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3p2() {
Structure struct = getStructure("B3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" +
"Aligned pack(2)\n" +
"Structure B3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB4p2() {
Structure struct = getStructure("B4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" +
"Aligned pack(2)\n" +
"Structure B4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 longlong 8 d \"\"\n" +
" 16 int:4(0) 1 e \"\"\n" +
"}\n" +
"Size = 20 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p2() {
Structure struct = getStructure("Z1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" +
"Aligned pack(2)\n" +
"Structure Z1p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 int:0(0) 1 \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p2x() {
Structure struct = getStructure("Z1p2x");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" +
"Aligned pack(2)\n" +
"Structure Z1p2x {\n" +
" 0 char 1 a \"\"\n" +
" 2 int:0(0) 1 \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
" 8 short:4(4) 1 d1 \"\"\n" +
" 9 short:4(0) 1 d2 \"\"\n" +
" 9 short:4(4) 1 d3 \"\"\n" +
" 10 short:4(0) 1 d4 \"\"\n" +
" 10 short:4(4) 1 d5 \"\"\n" +
" 11 short:4(0) 1 d6 \"\"\n" +
" 11 short:4(4) 1 d7 \"\"\n" +
" 12 short:0(0) 1 \"\"\n" +
" 12 ushort:6(0) 1 _b \"\"\n" +
" 14 int:8(0) 1 _c \"\"\n" +
" 18 short:4(0) 1 _d \"\"\n" +
" 18 short:4(4) 1 _d1 \"\"\n" +
" 19 short:4(0) 1 _d2 \"\"\n" +
" 19 short:4(4) 1 _d3 \"\"\n" +
" 20 short:4(0) 1 _d4 \"\"\n" +
" 20 short:4(4) 1 _d5 \"\"\n" +
" 21 short:4(0) 1 _d6 \"\"\n" +
" 21 short:4(4) 1 _d7 \"\"\n" +
"}\n" +
"Size = 22 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2p2() {
Structure struct = getStructure("Z2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" +
"Aligned pack(2)\n" +
"Structure Z2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 int:0(0) 1 \"\"\n" +
" 8 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p2() {
Structure struct = getStructure("Z3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" +
"Aligned pack(2)\n" +
"Structure Z3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 5 int:4(0) 1 d \"\"\n" +
" 7 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4p2() {
Structure struct = getStructure("Z4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" +
"Aligned pack(2)\n" +
"Structure Z4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5p2() {
Structure struct = getStructure("Z5p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" +
"Aligned pack(2)\n" +
"Structure Z5p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:12(0) 2 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x1p2() {
Structure struct = getStructure("x1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" +
"Aligned pack(2)\n" +
"Structure x1p2 {\n" +
" 0 char 1 a \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x2p2() {
Structure struct = getStructure("x2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" +
"Aligned pack(2)\n" +
"Structure x2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x3p2() {
Structure struct = getStructure("x3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" +
"Aligned pack(2)\n" +
"Structure x3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 short:0(0) 1 \"\"\n" +
" 2 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x4p2() {
Structure struct = getStructure("x4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" +
"Aligned pack(2)\n" +
"Structure x4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 int:27(0) 4 b \"\"\n" +
" 5 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5p4() {
Structure struct = getStructure("Z5p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" +
"Aligned pack(4)\n" +
"Structure Z5p4 {\n" +
" 0 char 1 a \"\"\n" +
" 2 ushort:12(0) 2 b \"\"\n" +
" 4 int:8(0) 1 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x1p4() {
Structure struct = getStructure("x1p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" +
"Aligned pack(4)\n" +
"Structure x1p4 {\n" +
" 0 char 1 a \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x2p4() {
Structure struct = getStructure("x2p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" +
"Aligned pack(4)\n" +
"Structure x2p4 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x3p4() {
Structure struct = getStructure("x3p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" +
"Aligned pack(4)\n" +
"Structure x3p4 {\n" +
" 0 char 1 a \"\"\n" +
" 4 short:0(0) 1 \"\"\n" +
" 4 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x4p4() {
Structure struct = getStructure("x4p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" +
"Aligned pack(4)\n" +
"Structure x4p4 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:27(0) 4 b \"\"\n" +
" 7 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsT1() {
Structure struct = getStructure("T1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/T1\n" +
"Aligned\n" +
"Structure T1 {\n" +
" 0 charTypedef 1 a \"\"\n" +
" 4 myEnum:3(0) 1 b \"\"\n" +
" 4 enumTypedef:3(3) 1 c \"\"\n" +
" 8 charTypedef:7(0) 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsT2() {
Structure struct = getStructure("T2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/T2\n" +
"Aligned\n" +
"Structure T2 {\n" +
" 0 charTypedef 1 a \"\"\n" +
" 4 intTypedef:17(0) 3 b \"\"\n" +
" 6 enumTypedef:3(1) 1 c \"\"\n" +
" 8 charTypedef:3(0) 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1() {
Structure struct = getStructure("S1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1\n" +
"Aligned\n" +
"Structure S1 {\n" +
" 0 B1 12 b1 \"\"\n" +
" 12 B2 8 b2 \"\"\n" +
" 20 Z1 12 z1 \"\"\n" +
" 32 Z2 12 z2 \"\"\n" +
" 48 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 56 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1p1() {
Structure struct = getStructure("S1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" +
"Aligned pack(1)\n" +
"Structure S1p1 {\n" +
" 0 B1 12 b1 \"\"\n" +
" 12 B2 8 b2 \"\"\n" +
" 20 Z1 12 z1 \"\"\n" +
" 32 Z2 12 z2 \"\"\n" +
" 44 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 52 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS2p1() {
Structure struct = getStructure("S2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" +
"Aligned pack(1)\n" +
"Structure S2p1 {\n" +
" 0 B1p1 9 b1p1 \"\"\n" +
" 9 B2p1 7 b2p1 \"\"\n" +
" 16 Z1p1 9 z1p1 \"\"\n" +
" 25 Z2p1 9 z2p1 \"\"\n" +
" 34 Z3p1 7 z3p1 \"\"\n" +
"}\n" +
"Size = 41 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1p2() {
Structure struct = getStructure("S1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" +
"Aligned pack(2)\n" +
"Structure S1p2 {\n" +
" 0 B1 12 b1 \"\"\n" +
" 12 B2 8 b2 \"\"\n" +
" 20 Z1 12 z1 \"\"\n" +
" 32 Z2 12 z2 \"\"\n" +
" 44 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 52 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS2p2() {
Structure struct = getStructure("S2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" +
"Aligned pack(2)\n" +
"Structure S2p2 {\n" +
" 0 B1p2 10 b1p2 \"\"\n" +
" 10 B2p2 8 b2p2 \"\"\n" +
" 18 Z1p2 10 z1p2 \"\"\n" +
" 28 Z2p2 10 z2p2 \"\"\n" +
" 38 Z3p2 8 z3p2 \"\"\n" +
"}\n" +
"Size = 46 Actual Alignment = 2", struct);
//@formatter:on
}
}

View file

@ -0,0 +1,108 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import org.junit.Test;
public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest {
private static DataTypeManager dataMgr;
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (MSVCUnionImplBitFieldTest.class) {
if (dataMgr == null) {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg);
dataMgr = new MyDataTypeManager("test", dataOrg);
}
return dataMgr;
}
}
@Test
public void testUnionBitFieldsU1() {
Union struct = getUnion("U1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1\n" +
"Aligned\n" +
"Union U1 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1z() {
Union struct = getUnion("U1z");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" +
"Aligned\n" +
"Union U1z {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p1() {
Union struct = getUnion("U1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" +
"Aligned pack(1)\n" +
"Union U1p1 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p1z() {
Union struct = getUnion("U1p1z");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" +
"Aligned pack(1)\n" +
"Union U1p1z {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p2() {
Union struct = getUnion("U1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" +
"Aligned pack(2)\n" +
"Union U1p2 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
}

View file

@ -0,0 +1,755 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import org.junit.Test;
public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBitFieldTest {
// NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2
private static DataTypeManager dataMgr;
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (StructureImplBigEndianBitFieldTest.class) {
if (dataMgr == null) {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg);
dataMgr = new MyDataTypeManager("test", dataOrg);
}
return dataMgr;
}
}
@Test
public void testStructureBitFieldsB1() {
Structure struct = getStructure("B1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1\n" +
"Aligned\n" +
"Structure B1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2() {
Structure struct = getStructure("B2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2\n" +
"Aligned\n" +
"Structure B2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3() {
Structure struct = getStructure("B3");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3\n" +
"Aligned\n" +
"Structure B3 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 3 char 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1() {
Structure struct = getStructure("Z1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" +
"Aligned\n" +
"Structure Z1 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 ushort:6(2) 1 b \"\"\n" +
" 4 int:8(2) 2 c \"\"\n" +
" 6 short:4(4) 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2() {
Structure struct = getStructure("Z2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" +
"Aligned\n" +
"Structure Z2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 short:4(4) 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3() {
Structure struct = getStructure("Z3");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" +
"Aligned\n" +
"Structure Z3 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
" 3 longlong:0(7) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4() {
Structure struct = getStructure("Z4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" +
"Aligned\n" +
"Structure Z4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 8 longlong:0(7) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5() {
Structure struct = getStructure("Z5");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" +
"Aligned\n" +
"Structure Z5 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 longlong:6(2) 1 b \"\"\n" +
" 4 int:8(2) 2 c \"\"\n" +
" 6 char 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ6() {
Structure struct = getStructure("Z6");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" +
"Aligned\n" +
"Structure Z6 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 longlong:6(2) 1 b \"\"\n" +
" 4 int:8(2) 2 c \"\"\n" +
" 6 char 1 d \"\"\n" +
" 7 longlong:6(2) 1 e \"\"\n" +
" 8 int:8(0) 1 f \"\"\n" +
" 9 char 1 g \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB1p1() {
Structure struct = getStructure("B1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" +
"Aligned pack(1)\n" +
"Structure B1p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2p1() {
Structure struct = getStructure("B2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" +
"Aligned pack(1)\n" +
"Structure B2p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3p1() {
Structure struct = getStructure("B3p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" +
"Aligned pack(1)\n" +
"Structure B3p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 3 char 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p1() {
Structure struct = getStructure("Z1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" +
"Aligned pack(1)\n" +
"Structure Z1p1 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 ushort:6(2) 1 b \"\"\n" +
" 4 int:8(2) 2 c \"\"\n" +
" 5 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 7 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2p1() {
Structure struct = getStructure("Z2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" +
"Aligned pack(1)\n" +
"Structure Z2p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 short:4(4) 1 d \"\"\n" +
"}\n" +
"Size = 5 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p1() {
Structure struct = getStructure("Z3p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" +
"Aligned pack(1)\n" +
"Structure Z3p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
" 3 longlong:0(7) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p1T() {
Structure struct = getStructure("Z3p1T");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" +
"Aligned\n" +
"Structure Z3p1T {\n" +
" 0 char 1 a \"\"\n" +
" 1 Z3p1 8 z3p1 \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4p1() {
Structure struct = getStructure("Z4p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" +
"Aligned pack(1)\n" +
"Structure Z4p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 8 longlong:0(7) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB1p2() {
Structure struct = getStructure("B1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" +
"Aligned pack(2)\n" +
"Structure B1p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2p2() {
Structure struct = getStructure("B2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" +
"Aligned pack(2)\n" +
"Structure B2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3p2() {
Structure struct = getStructure("B3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" +
"Aligned pack(2)\n" +
"Structure B3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 3 char 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB4p2() {
Structure struct = getStructure("B4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" +
"Aligned pack(2)\n" +
"Structure B4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 4 longlong 8 d \"\"\n" +
" 12 int:4(4) 1 e \"\"\n" +
"}\n" +
"Size = 14 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p2() {
Structure struct = getStructure("Z1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" +
"Aligned pack(2)\n" +
"Structure Z1p2 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 ushort:6(2) 1 b \"\"\n" +
" 4 int:8(2) 2 c \"\"\n" +
" 5 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p2x() {
Structure struct = getStructure("Z1p2x");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" +
"Aligned pack(2)\n" +
"Structure Z1p2x {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 ushort:6(2) 1 b \"\"\n" +
" 4 int:8(2) 2 c \"\"\n" +
" 5 short:4(6) 2 d \"\"\n" +
" 6 short:4(2) 1 d1 \"\"\n" +
" 6 short:4(6) 2 d2 \"\"\n" +
" 7 short:4(2) 1 d3 \"\"\n" +
" 7 short:4(6) 2 d4 \"\"\n" +
" 8 short:4(2) 1 d5 \"\"\n" +
" 8 short:4(6) 2 d6 \"\"\n" +
" 9 short:4(2) 1 d7 \"\"\n" +
" 10 short:0(7) 1 \"\"\n" +
" 10 ushort:6(2) 1 _b \"\"\n" +
" 10 int:8(2) 2 _c \"\"\n" +
" 11 short:4(6) 2 _d \"\"\n" +
" 12 short:4(2) 1 _d1 \"\"\n" +
" 12 short:4(6) 2 _d2 \"\"\n" +
" 13 short:4(2) 1 _d3 \"\"\n" +
" 13 short:4(6) 2 _d4 \"\"\n" +
" 14 short:4(2) 1 _d5 \"\"\n" +
" 14 short:4(6) 2 _d6 \"\"\n" +
" 15 short:4(2) 1 _d7 \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2p2() {
Structure struct = getStructure("Z2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" +
"Aligned pack(2)\n" +
"Structure Z2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 4 int:0(7) 1 \"\"\n" +
" 4 short:4(4) 1 d \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p2() {
Structure struct = getStructure("Z3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" +
"Aligned pack(2)\n" +
"Structure Z3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
" 3 longlong:0(7) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4p2() {
Structure struct = getStructure("Z4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" +
"Aligned pack(2)\n" +
"Structure Z4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(2) 1 b \"\"\n" +
" 1 int:8(2) 2 c \"\"\n" +
" 8 longlong:0(7) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5p2() {
Structure struct = getStructure("Z5p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" +
"Aligned pack(2)\n" +
"Structure Z5p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:12(4) 2 b \"\"\n" +
" 2 int:8(4) 2 c \"\"\n" +
" 8 longlong:0(7) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x1p2() {
Structure struct = getStructure("x1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" +
"Aligned pack(2)\n" +
"Structure x1p2 {\n" +
" 0 char 1 a \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x2p2() {
Structure struct = getStructure("x2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" +
"Aligned pack(2)\n" +
"Structure x2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(5) 4 b \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x3p2() {
Structure struct = getStructure("x3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" +
"Aligned pack(2)\n" +
"Structure x3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 short:0(7) 1 \"\"\n" +
" 2 int:27(5) 4 b \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x4p2() {
Structure struct = getStructure("x4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" +
"Aligned pack(2)\n" +
"Structure x4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(5) 4 b \"\"\n" +
" 4 longlong:0(7) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5p4() {
Structure struct = getStructure("Z5p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" +
"Aligned pack(4)\n" +
"Structure Z5p4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:12(4) 2 b \"\"\n" +
" 2 int:8(4) 2 c \"\"\n" +
" 8 longlong:0(7) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x1p4() {
Structure struct = getStructure("x1p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" +
"Aligned pack(4)\n" +
"Structure x1p4 {\n" +
" 0 char 1 a \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x2p4() {
Structure struct = getStructure("x2p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" +
"Aligned pack(4)\n" +
"Structure x2p4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(5) 4 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x3p4() {
Structure struct = getStructure("x3p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" +
"Aligned pack(4)\n" +
"Structure x3p4 {\n" +
" 0 char 1 a \"\"\n" +
" 2 short:0(7) 1 \"\"\n" +
" 2 int:27(5) 4 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x4p4() {
Structure struct = getStructure("x4p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" +
"Aligned pack(4)\n" +
"Structure x4p4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(5) 4 b \"\"\n" +
" 4 longlong:0(7) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsT1() {
Structure struct = getStructure("T1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/T1\n" +
"Aligned\n" +
"Structure T1 {\n" +
" 0 charTypedef 1 a \"\"\n" +
" 1 myEnum:3(5) 1 b \"\"\n" +
" 1 enumTypedef:3(2) 1 c \"\"\n" +
" 2 charTypedef:7(1) 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsT2() {
Structure struct = getStructure("T2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/T2\n" +
"Aligned\n" +
"Structure T2 {\n" +
" 0 charTypedef 1 a \"\"\n" +
" 1 intTypedef:17(7) 3 b \"\"\n" +
" 3 enumTypedef:3(4) 1 c \"\"\n" +
" 3 charTypedef:3(1) 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1() {
Structure struct = getStructure("S1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1\n" +
"Aligned\n" +
"Structure S1 {\n" +
" 0 B1 4 b1 \"\"\n" +
" 4 B2 4 b2 \"\"\n" +
" 8 Z1 8 z1 \"\"\n" +
" 16 Z2 8 z2 \"\"\n" +
" 24 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 32 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1p1() {
Structure struct = getStructure("S1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" +
"Aligned pack(1)\n" +
"Structure S1p1 {\n" +
" 0 B1 4 b1 \"\"\n" +
" 4 B2 4 b2 \"\"\n" +
" 8 Z1 8 z1 \"\"\n" +
" 16 Z2 8 z2 \"\"\n" +
" 24 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 32 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS2p1() {
Structure struct = getStructure("S2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" +
"Aligned pack(1)\n" +
"Structure S2p1 {\n" +
" 0 B1p1 4 b1p1 \"\"\n" +
" 4 B2p1 4 b2p1 \"\"\n" +
" 8 Z1p1 7 z1p1 \"\"\n" +
" 15 Z2p1 5 z2p1 \"\"\n" +
" 20 Z3p1 8 z3p1 \"\"\n" +
"}\n" +
"Size = 28 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1p2() {
Structure struct = getStructure("S1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" +
"Aligned pack(2)\n" +
"Structure S1p2 {\n" +
" 0 B1 4 b1 \"\"\n" +
" 4 B2 4 b2 \"\"\n" +
" 8 Z1 8 z1 \"\"\n" +
" 16 Z2 8 z2 \"\"\n" +
" 24 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 32 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS2p2() {
Structure struct = getStructure("S2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" +
"Aligned pack(2)\n" +
"Structure S2p2 {\n" +
" 0 B1p2 4 b1p2 \"\"\n" +
" 4 B2p2 4 b2p2 \"\"\n" +
" 8 Z1p2 8 z1p2 \"\"\n" +
" 16 Z2p2 6 z2p2 \"\"\n" +
" 22 Z3p2 8 z3p2 \"\"\n" +
"}\n" +
"Size = 30 Actual Alignment = 2", struct);
//@formatter:on
}
}

View file

@ -0,0 +1,816 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import org.junit.Test;
import resources.ResourceManager;
public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImplBitFieldTest {
// NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2)
private static DataTypeManager dataMgr;
@Override
public void setUp() throws Exception {
super.setUp();
// uncomment to generate datatype archive
// writeArchive();
}
private void writeArchive() throws IOException {
URL resource = ResourceManager.getResource(C_SOURCE_FILE);
File f = new File(resource.getPath() + ".gdt");
if (f.exists()) {
f.delete();
}
FileDataTypeManager fileDtMgr = FileDataTypeManager.createFileArchive(f);
int txId = fileDtMgr.startTransaction("Save Datatypes");
try {
Iterator<Composite> composites = getDataTypeManager().getAllComposites();
while (composites.hasNext()) {
fileDtMgr.addDataType(composites.next(), null);
}
}
finally {
fileDtMgr.endTransaction(txId, true);
}
fileDtMgr.save();
fileDtMgr.close();
}
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (StructureImplBigEndianBitFieldTest.class) {
if (dataMgr == null) {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg);
dataMgr = new MyDataTypeManager("test", dataOrg);
}
return dataMgr;
}
}
@Test
public void testStructureBitFieldsB1() {
Structure struct = getStructure("B1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1\n" +
"Aligned\n" +
"Structure B1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2() {
Structure struct = getStructure("B2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2\n" +
"Aligned\n" +
"Structure B2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3() {
Structure struct = getStructure("B3");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3\n" +
"Aligned\n" +
"Structure B3 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" + // gcc groups with previous non-bitfield
" 1 int:8(6) 2 c \"\"\n" +
" 3 char 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1() {
Structure struct = getStructure("Z1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" +
"Aligned\n" +
"Structure Z1 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(6) 2 c \"\"\n" +
" 6 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2() {
Structure struct = getStructure("Z2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" +
"Aligned\n" +
"Structure Z2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3() {
Structure struct = getStructure("Z3");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" +
"Aligned\n" +
"Structure Z3 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
" 3 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4() {
Structure struct = getStructure("Z4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" +
"Aligned\n" +
"Structure Z4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5() {
Structure struct = getStructure("Z5");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" +
"Aligned\n" +
"Structure Z5 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 longlong:6(0) 1 b \"\"\n" +
" 4 int:8(6) 2 c \"\"\n" +
" 6 char 1 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ6() {
Structure struct = getStructure("Z6");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" +
"Aligned\n" +
"Structure Z6 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 longlong:6(0) 1 b \"\"\n" +
" 4 int:8(6) 2 c \"\"\n" +
" 6 char 1 d \"\"\n" +
" 7 longlong:6(0) 1 e \"\"\n" +
" 8 int:8(0) 1 f \"\"\n" +
" 9 char 1 g \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB1p1() {
Structure struct = getStructure("B1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" +
"Aligned pack(1)\n" +
"Structure B1p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2p1() {
Structure struct = getStructure("B2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" +
"Aligned pack(1)\n" +
"Structure B2p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3p1() {
Structure struct = getStructure("B3p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" +
"Aligned pack(1)\n" +
"Structure B3p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 3 char 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p1() {
Structure struct = getStructure("Z1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" +
"Aligned pack(1)\n" +
"Structure Z1p1 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(6) 2 c \"\"\n" +
" 5 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 7 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2p1() {
Structure struct = getStructure("Z2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" +
"Aligned pack(1)\n" +
"Structure Z2p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 5 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p1() {
Structure struct = getStructure("Z3p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" +
"Aligned pack(1)\n" +
"Structure Z3p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
" 3 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p1T() {
Structure struct = getStructure("Z3p1T");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" +
"Aligned\n" +
"Structure Z3p1T {\n" +
" 0 char 1 a \"\"\n" +
" 1 Z3p1 8 z3p1 \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4p1() {
Structure struct = getStructure("Z4p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" +
"Aligned pack(1)\n" +
"Structure Z4p1 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 9 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB1p2() {
Structure struct = getStructure("B1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" +
"Aligned pack(2)\n" +
"Structure B1p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB2p2() {
Structure struct = getStructure("B2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" +
"Aligned pack(2)\n" +
"Structure B2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB3p2() {
Structure struct = getStructure("B3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" +
"Aligned pack(2)\n" +
"Structure B3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 3 char 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsB4p2() {
Structure struct = getStructure("B4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" +
"Aligned pack(2)\n" +
"Structure B4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 4 longlong 8 d \"\"\n" +
" 12 int:4(0) 1 e \"\"\n" +
"}\n" +
"Size = 14 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p2() {
Structure struct = getStructure("Z1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" +
"Aligned pack(2)\n" +
"Structure Z1p2 {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(6) 2 c \"\"\n" +
" 5 short:4(6) 2 d \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ1p2x() {
Structure struct = getStructure("Z1p2x");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" +
"Aligned pack(2)\n" +
"Structure Z1p2x {\n" +
" 0 char 1 a \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 ushort:6(0) 1 b \"\"\n" +
" 4 int:8(6) 2 c \"\"\n" +
" 5 short:4(6) 2 d \"\"\n" +
" 6 short:4(2) 1 d1 \"\"\n" +
" 6 short:4(6) 2 d2 \"\"\n" +
" 7 short:4(2) 1 d3 \"\"\n" +
" 7 short:4(6) 2 d4 \"\"\n" +
" 8 short:4(2) 1 d5 \"\"\n" +
" 8 short:4(6) 2 d6 \"\"\n" +
" 9 short:4(2) 1 d7 \"\"\n" +
" 10 short:0(0) 1 \"\"\n" +
" 10 ushort:6(0) 1 _b \"\"\n" +
" 10 int:8(6) 2 _c \"\"\n" +
" 11 short:4(6) 2 _d \"\"\n" +
" 12 short:4(2) 1 _d1 \"\"\n" +
" 12 short:4(6) 2 _d2 \"\"\n" +
" 13 short:4(2) 1 _d3 \"\"\n" +
" 13 short:4(6) 2 _d4 \"\"\n" +
" 14 short:4(2) 1 _d5 \"\"\n" +
" 14 short:4(6) 2 _d6 \"\"\n" +
" 15 short:4(2) 1 _d7 \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ2p2() {
Structure struct = getStructure("Z2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" +
"Aligned pack(2)\n" +
"Structure Z2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 4 int:0(0) 1 \"\"\n" +
" 4 short:4(0) 1 d \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ3p2() {
Structure struct = getStructure("Z3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" +
"Aligned pack(2)\n" +
"Structure Z3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 2 int:4(6) 2 d \"\"\n" +
" 3 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ4p2() {
Structure struct = getStructure("Z4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" +
"Aligned pack(2)\n" +
"Structure Z4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:6(0) 1 b \"\"\n" +
" 1 int:8(6) 2 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5p2() {
Structure struct = getStructure("Z5p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" +
"Aligned pack(2)\n" +
"Structure Z5p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:12(0) 2 b \"\"\n" +
" 2 int:8(4) 2 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x1p2() {
Structure struct = getStructure("x1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" +
"Aligned pack(2)\n" +
"Structure x1p2 {\n" +
" 0 char 1 a \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x2p2() {
Structure struct = getStructure("x2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" +
"Aligned pack(2)\n" +
"Structure x2p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x3p2() {
Structure struct = getStructure("x3p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" +
"Aligned pack(2)\n" +
"Structure x3p2 {\n" +
" 0 char 1 a \"\"\n" +
" 2 short:0(0) 1 \"\"\n" +
" 2 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 6 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x4p2() {
Structure struct = getStructure("x4p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" +
"Aligned pack(2)\n" +
"Structure x4p2 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(0) 4 b \"\"\n" +
" 4 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsZ5p4() {
Structure struct = getStructure("Z5p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" +
"Aligned pack(4)\n" +
"Structure Z5p4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 ushort:12(0) 2 b \"\"\n" +
" 2 int:8(4) 2 c \"\"\n" +
" 8 longlong:0(0) 1 \"\"\n" +
" 8 char 1 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x1p4() {
Structure struct = getStructure("x1p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" +
"Aligned pack(4)\n" +
"Structure x1p4 {\n" +
" 0 char 1 a \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x2p4() {
Structure struct = getStructure("x2p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" +
"Aligned pack(4)\n" +
"Structure x2p4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x3p4() {
Structure struct = getStructure("x3p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" +
"Aligned pack(4)\n" +
"Structure x3p4 {\n" +
" 0 char 1 a \"\"\n" +
" 2 short:0(0) 1 \"\"\n" +
" 2 int:27(0) 4 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFields_x4p4() {
Structure struct = getStructure("x4p4");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" +
"Aligned pack(4)\n" +
"Structure x4p4 {\n" +
" 0 char 1 a \"\"\n" +
" 1 int:27(0) 4 b \"\"\n" +
" 4 longlong:0(0) 1 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsT1() {
Structure struct = getStructure("T1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/T1\n" +
"Aligned\n" +
"Structure T1 {\n" +
" 0 charTypedef 1 a \"\"\n" +
" 1 myEnum:3(0) 1 b \"\"\n" +
" 1 enumTypedef:3(3) 1 c \"\"\n" +
" 2 charTypedef:7(0) 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsT2() {
Structure struct = getStructure("T2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/T2\n" +
"Aligned\n" +
"Structure T2 {\n" +
" 0 charTypedef 1 a \"\"\n" +
" 1 intTypedef:17(0) 3 b \"\"\n" +
" 3 enumTypedef:3(1) 1 c \"\"\n" +
" 3 charTypedef:3(4) 1 d \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1() {
Structure struct = getStructure("S1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1\n" +
"Aligned\n" +
"Structure S1 {\n" +
" 0 B1 4 b1 \"\"\n" +
" 4 B2 4 b2 \"\"\n" +
" 8 Z1 8 z1 \"\"\n" +
" 16 Z2 8 z2 \"\"\n" +
" 24 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 32 Actual Alignment = 8", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1p1() {
Structure struct = getStructure("S1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" +
"Aligned pack(1)\n" +
"Structure S1p1 {\n" +
" 0 B1 4 b1 \"\"\n" +
" 4 B2 4 b2 \"\"\n" +
" 8 Z1 8 z1 \"\"\n" +
" 16 Z2 8 z2 \"\"\n" +
" 24 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 32 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS2p1() {
Structure struct = getStructure("S2p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" +
"Aligned pack(1)\n" +
"Structure S2p1 {\n" +
" 0 B1p1 4 b1p1 \"\"\n" +
" 4 B2p1 4 b2p1 \"\"\n" +
" 8 Z1p1 7 z1p1 \"\"\n" +
" 15 Z2p1 5 z2p1 \"\"\n" +
" 20 Z3p1 8 z3p1 \"\"\n" +
"}\n" +
"Size = 28 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS1p2() {
Structure struct = getStructure("S1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" +
"Aligned pack(2)\n" +
"Structure S1p2 {\n" +
" 0 B1 4 b1 \"\"\n" +
" 4 B2 4 b2 \"\"\n" +
" 8 Z1 8 z1 \"\"\n" +
" 16 Z2 8 z2 \"\"\n" +
" 24 Z3 8 z3 \"\"\n" +
"}\n" +
"Size = 32 Actual Alignment = 2", struct);
//@formatter:on
}
@Test
public void testStructureBitFieldsS2p2() {
Structure struct = getStructure("S2p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" +
"Aligned pack(2)\n" +
"Structure S2p2 {\n" +
" 0 B1p2 4 b1p2 \"\"\n" +
" 4 B2p2 4 b2p2 \"\"\n" +
" 8 Z1p2 8 z1p2 \"\"\n" +
" 16 Z2p2 6 z2p2 \"\"\n" +
" 22 Z3p2 8 z3p2 \"\"\n" +
"}\n" +
"Size = 30 Actual Alignment = 2", struct);
//@formatter:on
}
// @Test
// public void testStructureBitFieldsFOO() {
// Structure struct = getStructure("Z3p1T");
//
// System.out.println(struct.toString());
//
// DataTypeManager dtm = struct.getDataTypeManager();
// if (dtm instanceof StandAloneDataTypeManager) {
// ((StandAloneDataTypeManager) dtm).startTransaction("TEST");
// }
// else if (dtm instanceof ProgramDataTypeManager) {
// ((ProgramDataTypeManager) dtm).getProgram().startTransaction("TEST");
// }
//
// ArrayList<InternalDataTypeComponent> components =
// (ArrayList<InternalDataTypeComponent>) getInstanceField("components", struct);
//
// AlignedStructurePacker.packComponents(struct, components);
//
// //@formatter:off
// CompositeTestUtils.assertExpectedComposite(this, "", struct);
// //@formatter:on
// }
}

View file

@ -0,0 +1,109 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import org.junit.Test;
public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFieldTest {
// NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2
private static DataTypeManager dataMgr;
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (StructureImplBigEndianBitFieldTest.class) {
if (dataMgr == null) {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg);
dataMgr = new MyDataTypeManager("test", dataOrg);
}
return dataMgr;
}
}
@Test
public void testUnionBitFieldsU1() {
Union struct = getUnion("U1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1\n" +
"Aligned\n" +
"Union U1 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1z() {
Union struct = getUnion("U1z");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" +
"Aligned\n" +
"Union U1z {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" + // has no impact
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p1() {
Union struct = getUnion("U1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" +
"Aligned pack(1)\n" +
"Union U1p1 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p1z() {
Union struct = getUnion("U1p1z");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" +
"Aligned pack(1)\n" +
"Union U1p1z {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" + // has no impact
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p2() {
Union struct = getUnion("U1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" +
"Aligned pack(2)\n" +
"Union U1p2 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 2 Actual Alignment = 2", struct);
//@formatter:on
}
}

View file

@ -0,0 +1,109 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import org.junit.Test;
public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitFieldTest {
// NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2)
private static DataTypeManager dataMgr;
@Override
protected DataTypeManager getDataTypeManager() {
synchronized (StructureImplBigEndianBitFieldTest.class) {
if (dataMgr == null) {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg);
dataMgr = new MyDataTypeManager("test", dataOrg);
}
return dataMgr;
}
}
@Test
public void testUnionBitFieldsU1() {
Union struct = getUnion("U1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1\n" +
"Aligned\n" +
"Union U1 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1z() {
Union struct = getUnion("U1z");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" +
"Aligned\n" +
"Union U1z {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" + // has no impact
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 4 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p1() {
Union struct = getUnion("U1p1");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" +
"Aligned pack(1)\n" +
"Union U1p1 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p1z() {
Union struct = getUnion("U1p1z");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" +
"Aligned pack(1)\n" +
"Union U1p1z {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" + // has no impact
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct);
//@formatter:on
}
@Test
public void testUnionBitFieldsU1p2() {
Union struct = getUnion("U1p2");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" +
"Aligned pack(2)\n" +
"Union U1p2 {\n" +
" 0 int:4(0) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" +
"}\n" +
"Size = 2 Actual Alignment = 2", struct);
//@formatter:on
}
}

View file

@ -0,0 +1,184 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include "bitfields.h"
struct B1 B1 = { 5, 0x2A, -1, 0xA };
struct B2 B2 = { 5, 0x2A, -1, 0xA };
struct B3 B3 = { 5, 0x2A, -1, 0xA };
struct Z1 Z1 = { 5, 0x2A, -1, 0xA };
struct Z2 Z2 = { 5, 0x2A, -1, 0xA };
struct Z3 Z3 = { 5, 0x2A, -1, 0xA };
struct Z4 Z4 = { 5, 0x2A, -1, 0xA };
struct Z5 Z5 = { 5, 0x2A, -1, 0xA };
struct Z6 Z6 = { 5, 0x2A, -1, 0xA, 0x2A, -1, 0xA };
struct B1p1 B1p1 = { 5, 0x2A, -1, 0xA };
struct B2p1 B2p1 = { 5, 0x2A, -1, 0xA };
struct B3p1 B3p1 = { 5, 0x2A, -1, 0xA };
struct Z1p1 Z1p1 = { 5, 0x2A, -1, 0xA };
struct Z2p1 Z2p1 = { 5, 0x2A, -1, 0xA };
struct Z3p1 Z3p1 = { 5, 0x2A, -1, 0xA };
struct Z3p1T Z3p1T = { 7, { 5, 0x2A, -1, 0xA }};
struct Z4p1 Z4p1 = { 5, 0x2A, -1, 0xA };
struct B1p2 B1p2 = { 5, 0x2A, -1, 0xA };
struct B2p2 B2p2 = { 5, 0x2A, -1, 0xA };
struct B3p2 B3p2 = { 5, 0x2A, -1, 0xA };
struct B4p2 B4p2 = { 5, 0x2A, -1, 0x5555555555555555, 0xA };
struct Z1p2 Z1p2 = { 5, 0x2A, -1, 0xA };
struct Z1p2x Z1p2x = { 5, 0x2A, -1, 0xA, -1, 0, -1, 0, -1, 0, -1, 0x2A, -1, 0xA, -1, 0, -1, 0, -1, 0, -1 };
struct Z2p2 Z2p2 = { 5, 0x2A, -1, 0xA };
struct Z3p2 Z3p2 = { 5, 0x2A, -1, 0xA };
struct Z4p2 Z4p2 = { 5, 0x2A, -1, 0xA };
struct Z5p2 Z5p2 = { 5, 0x2A, -1, 0xA };
struct x1p2 x1p2 = { 5 };
struct x2p2 x2p2 = { 5, 0x2A };
struct x3p2 x3p2 = { 5, 0x2A };
struct x4p2 x4p2 = { 5, 0x2A };
struct Z5p4 Z5p4 = { 5, 0x2A, -1, 0xA };
struct x1p4 x1p4 = { 5 };
struct x2p4 x2p4 = { 5, 0x2A };
struct x3p4 x3p4 = { 5, 0x2A };
struct x4p4 x4p4 = { 5, 0x2A };
struct S1 S1 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } };
struct S1p1 S1p1 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } };
struct S2p1 S2p1 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } };
struct S1p2 S1p2 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } };
struct S2p2 S2p2 = { { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA }, { 5, 0x2A, -1, 0xA } };
struct T1 T1 = { 5, TWO, THREE, 1 };
struct T2 T2 = { 5, 0x2A, THREE, 1 };
union U1 U1;
union U1z U1z;
union U1p1 U1p1;
union U1p1z U1p1z;
union U1p2 U1p2;
struct SUp1 SUp1;
int main(int argc, char *argv[]) {
printf("len B1: %d\n", sizeof(struct B1));
printf("len B2: %d\n", sizeof(struct B2));
printf("len B3: %d\n", sizeof(struct B3));
printf("len Z1: %d\n", sizeof(struct Z1));
printf("len Z2: %d\n", sizeof(struct Z2));
printf("len Z3: %d\n", sizeof(struct Z3));
printf("len Z4: %d\n", sizeof(struct Z4));
printf("len Z5: %d\n", sizeof(struct Z5));
printf("len Z6: %d\n", sizeof(struct Z6));
printf("len B1p1: %d\n", sizeof(struct B1p1));
printf("len B2p1: %d\n", sizeof(struct B2p1));
printf("len B3p1: %d\n", sizeof(struct B3p1));
printf("len Z1p1: %d\n", sizeof(struct Z1p1));
printf("len Z2p1: %d\n", sizeof(struct Z2p1));
printf("len Z3p1: %d\n", sizeof(struct Z3p1));
printf("len Z3p1T: %d\n", sizeof(struct Z3p1T));
printf("len Z4p1: %d\n", sizeof(struct Z4p1));
printf("len B1p2: %d\n", sizeof(struct B1p2));
printf("len B2p2: %d\n", sizeof(struct B2p2));
printf("len B3p2: %d\n", sizeof(struct B3p2));
printf("len B4p2: %d\n", sizeof(struct B4p2));
printf("len Z1p2: %d\n", sizeof(struct Z1p2));
printf("len Z1p2x: %d\n", sizeof(struct Z1p2x));
printf("len Z2p2: %d\n", sizeof(struct Z2p2));
printf("len Z3p2: %d\n", sizeof(struct Z3p2));
printf("len Z4p2: %d\n", sizeof(struct Z4p2));
printf("len Z5p2: %d\n", sizeof(struct Z5p2));
printf("len x1p2: %d\n", sizeof(struct x1p2));
printf("len x2p2: %d\n", sizeof(struct x2p2));
printf("len x3p2: %d\n", sizeof(struct x3p2));
printf("len x4p2: %d\n", sizeof(struct x4p2));
printf("len Z5p4: %d\n", sizeof(struct Z5p4));
printf("len x1p4: %d\n", sizeof(struct x1p4));
printf("len x2p4: %d\n", sizeof(struct x2p4));
printf("len x3p4: %d\n", sizeof(struct x3p4));
printf("len x4p4: %d\n", sizeof(struct x4p4));
printf("len S1: %d\n", sizeof(struct S1));
printf("len S1p1: %d\n", sizeof(struct S1p1));
printf("len S2p1: %d\n", sizeof(struct S2p1));
printf("len S1p2: %d\n", sizeof(struct S1p2));
printf("len S2p2: %d\n", sizeof(struct S2p2));
printf("len T1: %d\n", sizeof(struct T1));
printf("len T2: %d\n", sizeof(struct T2));
printf("len U1: %d\n", sizeof(union U1));
printf("len U1z: %d\n", sizeof(union U1z));
printf("len U1p1: %d\n", sizeof(union U1p1));
printf("len U1p1z: %d\n", sizeof(union U1p1z));
printf("len U1p2: %d\n", sizeof(union U1p2));
printf("len SUp1: %d\n", sizeof(struct SUp1));
}

View file

@ -0,0 +1,445 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Verify bitfield grouping and alignment without zero-length bitfields
struct B1 {
char a;
unsigned short b:6; // gcc groups with previous non-bitfield
int c:8; // gcc groups with previous two fields (including non-bitfield)
short d:4;
};
struct B2 {
char a;
unsigned short b:6; // gcc groups with previous non-bitfield
int c:8;
int d:4;
};
struct B3 {
char a;
unsigned short b:6; // gcc groups with previous non-bitfield
int c:8;
char d; // gcc groups with int bit-field
};
// Verify bitfield grouping and alignment with zero-length bitfields
struct Z1 {
char a;
int :0; // MSVC ignores field, gcc forces break and does not combine with previous field
unsigned short b:6;
int c:8;
short d:4;
};
struct Z2 {
char a;
unsigned short b:6; // gcc groups with previous non-bitfield
int c:8;
int :0;
short d:4;
};
struct Z3 {
char a;
unsigned short b:6; // gcc groups with previous non-bitfield
int c:8; // gcc groups with previous two fields (including non-bitfield)
int d:4;
long long :0; // trailing :0 imposes alignment onto structure
};
struct Z4 {
char a;
unsigned short b:6; // gcc groups with previous non-bitfield
int c:8; // gcc groups with previous two fields (including non-bitfield)
long long :0; // forced alignment of non-bitfield
char d;
};
struct Z5 {
char a;
int :0;
long long b:6;
int c:8;
char d;
};
struct Z6 {
char a;
int :0;
long long b:6;
int c:8;
char d;
long long e:6;
int f:8;
char g;
};
#pragma pack(1)
// Verify bitfield grouping and alignment without zero-length bitfields
struct B1p1 {
char a;
unsigned short b:6;
int c:8;
short d:4;
};
struct B2p1 {
char a;
unsigned short b:6;
int c:8;
int d:4;
};
struct B3p1 {
char a;
unsigned short b:6;
int c:8;
char d; // gcc groups with int bit-field
};
// Verify bitfield grouping and alignment with zero-length bitfields
struct Z1p1 {
char a;
int :0; // MSVC ignores field
unsigned short b:6;
int c:8;
short d:4;
};
struct Z2p1 {
char a;
unsigned short b:6;
int c:8;
int :0;
short d:4;
};
struct Z3p1 {
char a;
unsigned short b:6;
int c:8;
int d:4;
long long :0; // trailing :0 (ignore when packing ?) - case needs more testing
};
struct Z4p1 {
char a;
unsigned short b:6;
int c:8;
long long :0; // forced alignment of non-bitfield
char d;
};
#pragma pack()
// packed structure contained within default aligned structure
struct Z3p1T {
char a;
struct Z3p1 z3p1;
};
#pragma pack(2)
// Verify bitfield grouping and alignment without zero-length bitfields
struct B1p2 {
char a;
unsigned short b:6;
int c:8;
short d:4;
};
struct B2p2 {
char a;
unsigned short b:6;
int c:8;
int d:4;
};
struct B3p2 {
char a;
unsigned short b:6;
int c:8;
char d; // gcc groups with int bit-field
};
struct B4p2 {
char a;
unsigned short b:6;
int c:8;
long long d;
int e:4;
};
// Verify bitfield grouping and alignment with zero-length bitfields
struct Z1p2 {
char a;
int :0; // MSVC ignores field
unsigned short b:6;
int c:8;
short d:4; // NOTE: gcc appears ignore short alignment constraint due to int :0 ???
};
struct Z1p2x {
char a;
int :0; // MSVC ignores field
unsigned short b:6;
int c:8;
short d:4; // NOTE: gcc appears ignore short alignment constraint due to int :0 ???
short d1:4;
short d2:4;
short d3:4;
short d4:4;
short d5:4;
short d6:4;
short d7:4;
short :0;
unsigned short _b:6;
int _c:8;
short _d:4; // NOTE: gcc appears ignore short alignment constraint due to int :0 ???
short _d1:4;
short _d2:4;
short _d3:4;
short _d4:4;
short _d5:4;
short _d6:4;
short _d7:4;
};
struct Z2p2 {
char a;
unsigned short b:6;
int c:8;
int :0;
short d:4;
};
struct Z3p2 {
char a;
unsigned short b:6;
int c:8;
int d:4;
long long :0; // trailing :0 (ignore when packing ?) - case needs more testing
};
struct Z4p2 {
char a;
unsigned short b:6;
int c:8;
long long :0; // forced alignment of non-bitfield
char d;
};
struct Z5p2 {
char a;
unsigned short b:12;
int c:8;
long long :0; // forced alignment of non-bitfield
char d;
};
struct x1p2 {
char a;
};
struct x2p2 {
char a;
int b:27;
};
struct x3p2 {
char a;
short :0;
int b:27;
};
struct x4p2 {
char a;
int b:27;
long long :0;
};
#pragma pack()
#pragma pack(4)
struct Z5p4 {
char a;
unsigned short b:12;
int c:8;
long long :0; // forced alignment of non-bitfield
char d;
};
struct x1p4 {
char a;
};
struct x2p4 {
char a;
int b:27;
};
struct x3p4 {
char a;
short :0;
int b:27;
};
struct x4p4 {
char a;
int b:27;
long long :0;
};
#pragma pack()
// Structures within structures
struct S1 {
struct B1 b1;
struct B2 b2;
struct Z1 z1;
struct Z2 z2;
struct Z3 z3;
};
#pragma pack(1)
struct S1p1 {
struct B1 b1;
struct B2 b2;
struct Z1 z1;
struct Z2 z2;
struct Z3 z3;
};
struct S2p1 {
struct B1p1 b1p1;
struct B2p1 b2p1;
struct Z1p1 z1p1;
struct Z2p1 z2p1;
struct Z3p1 z3p1;
};
#pragma pack()
#pragma pack(2)
struct S1p2 {
struct B1 b1;
struct B2 b2;
struct Z1 z1;
struct Z2 z2;
struct Z3 z3;
};
struct S2p2 {
struct B1p2 b1p2;
struct B2p2 b2p2;
struct Z1p2 z1p2;
struct Z2p2 z2p2;
struct Z3p2 z3p2;
};
#pragma pack()
enum myEnum { ONE, TWO, THREE };
typedef enum myEnum enumTypedef;
typedef int intTypedef;
typedef char charTypedef;
typedef short shortTypedef;
struct T1 {
charTypedef a;
enum myEnum b:3;
enumTypedef c:3;
charTypedef d:7;
};
struct T2 {
charTypedef a;
intTypedef b:17;
enumTypedef c:3;
charTypedef d:3;
};
// Unions
union U1 {
int a:4;
int b:2;
};
union U1z {
int a:4;
long long :0;
int b:2;
};
#pragma pack(1)
union U1p1 {
int a:4;
int b:2;
};
union U1p1z {
int a:4;
long long :0;
int b:2;
};
struct SUp1 {
char a;
union U1p1z u;
};
#pragma pack(2)
union U1p2 {
int a:4;
int b:2;
};
#pragma pack()

View file

@ -15,6 +15,12 @@ dependencies {
compile project(":Base")
testCompile "org.jmockit:jmockit:1.44"
// Demangler Analyzer needs to find MicrosoftDemangler
compile project(":MicrosoftDemangler")
testCompile project(path: ':Base', configuration: 'testArtifacts')
testCompile project(path: ':SoftwareModeling', configuration: 'testArtifacts')
}
/**

View file

@ -130,14 +130,14 @@ public class PdbAnalyzer extends AbstractAnalyzer {
boolean parsePdb(File pdb, Program program, AutoAnalysisManager mgr, TaskMonitor monitor,
MessageLog log) {
DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService();
PdbParserNEW parser = new PdbParserNEW(pdb, program, dataTypeManagerService, true);
PdbParserNEW parser = new PdbParserNEW(pdb, program, dataTypeManagerService, true, monitor);
String message;
try {
parser.parse();
parser.openDataTypeArchives();
parser.applyTo(monitor, log);
parser.applyTo(log);
return true;
}
catch (PdbException e) {
@ -150,7 +150,11 @@ public class PdbAnalyzer extends AbstractAnalyzer {
return false;
}
catch (Exception e) {
Msg.showError(this, null, ERROR_TITLE, e.getMessage(), e);
String msg = e.getMessage();
if (msg == null) {
msg = e.toString();
}
Msg.showError(this, null, ERROR_TITLE, msg, e);
return false;
}

View file

@ -19,46 +19,76 @@ import java.util.*;
import org.xml.sax.SAXParseException;
import ghidra.app.util.bin.format.pdb.PdbParserNEW.PdbXmlMember;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.data.*;
import ghidra.graph.*;
import ghidra.graph.algo.GraphNavigator;
import ghidra.graph.jung.JungDirectedGraph;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.xml.XmlUtilities;
import ghidra.xml.*;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
public class ApplyDataTypes {
private PdbParserNEW pdbParser;
private boolean isClasses;
private MessageLog log;
private List<XmlTreeNode> todo = new ArrayList<>();
private HashMap<String, CompositeDefinition> compositeQueue = new HashMap<>();
/**
* Construct a PDB XML datatype or class parser. This will pre-process each datatype element and cache
* a properly sized composite for subsequent type reference. The full parse will not be completed
* until the {@link #applyTo(TaskMonitor)} method is invoked after all types and classes have been
* pre-processed or applied.
* Construct a PDB XML datatype or class parser. The {@link #preProcessDataTypeList(XmlPullParser, boolean, TaskMonitor)}
* method must be used to injest member elements from the pull parser to populate the set of type to be parsed.
* The full parse will not be completed until the {@link #applyTo(TaskMonitor)} method is invoked after all types
* and classes have been pre-processed or applied.
* @param pdbParser PDB parser object
* @param xmlParser XML parser positioned immediately after datatypes or classes element
* @param isClasses true if processing classes, false if composite datatypes
* @param monitor task progress monitor
* @param log message log used during construction and subsequent method invocations
* @throws CancelledException if monitor is cancelled
* @throws SAXParseException PDB XML parse failure
*/
ApplyDataTypes(PdbParserNEW pdbParser, XmlPullParser xmlParser, boolean isClasses,
TaskMonitor monitor, MessageLog log) throws CancelledException, SAXParseException {
ApplyDataTypes(PdbParserNEW pdbParser, MessageLog log)
throws CancelledException, SAXParseException {
this.pdbParser = pdbParser;
this.isClasses = isClasses;
this.log = log;
// Build todo list and cache preliminary composite definitions
preProcessDataTypeList(xmlParser, monitor);
}
void dispose() {
todo.clear();
compositeQueue.clear();
}
private List<CompositeDefinition> getCompositeDefinitionsInpostDependencyOrder(
TaskMonitor monitor) {
JungDirectedGraph<CompositeDefinition, GEdge<CompositeDefinition>> graph =
new JungDirectedGraph<>();
for (CompositeDefinition compositeDefinition : compositeQueue.values()) {
graph.addVertex(compositeDefinition);
for (PdbMember m : compositeDefinition.memberList) {
String name = m.memberDataTypeName;
int index = name.indexOf('[');
if (index > 0) {
name = name.substring(0, index).trim();
}
CompositeDefinition child = compositeQueue.get(name);
if (child != null) {
graph.addEdge(new DefaultGEdge<>(compositeDefinition, child));
}
}
}
// FIXME: GraphAlgorithms.findCircuits(graph, monitor);
List<CompositeDefinition> verticesInPostOrder =
GraphAlgorithms.getVerticesInPostOrder(graph, GraphNavigator.topDownNavigator());
return verticesInPostOrder;
}
/**
@ -68,75 +98,67 @@ public class ApplyDataTypes {
*/
void buildDataTypes(TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Order PDB datatypes... ");
List<CompositeDefinition> verticesInPostOrder =
getCompositeDefinitionsInpostDependencyOrder(monitor);
monitor.setMessage("Building PDB datatypes... ");
for (XmlTreeNode node : todo) {
for (CompositeDefinition compositeDefinition : verticesInPostOrder) {
monitor.checkCanceled();
XmlElement elem = node.getStartElement();
String name = SymbolUtilities.replaceInvalidChars(elem.getAttribute("name"), false);
String kind = isClasses ? PdbParserNEW.STRUCTURE_KIND : elem.getAttribute("kind");
int length = XmlUtilities.parseInt(elem.getAttribute("length"));
// namespace qualified name used for cache lookups
DataType cachedDataType = pdbParser.getCachedDataType(name);
DataType cachedDataType = pdbParser.getCachedDataType(compositeDefinition.name);
if (!(cachedDataType instanceof Composite) ||
!cachedDataType.getCategoryPath().equals(pdbParser.getCategory(name, true)) ||
!pdbParser.isCorrectKind(cachedDataType, kind)) {
log.appendMsg("Error: Conflicting data type name: " + name);
!cachedDataType.getCategoryPath().equals(
pdbParser.getCategory(compositeDefinition.name, true)) ||
!pdbParser.isCorrectKind(cachedDataType, compositeDefinition.kind)) {
log.appendMsg("Error: Conflicting data type name: " + compositeDefinition.name);
continue;
}
Composite composite = (Composite) cachedDataType;
PdbUtil.clearComponents(composite);
if (!CompositeMember.applyDataTypeMembers(pdbParser, composite, length, node,
monitor)) {
if (!DefaultCompositeMember.applyDataTypeMembers(composite, compositeDefinition.isClass,
compositeDefinition.length, getNormalMembersOnly(compositeDefinition),
msg -> Msg.warn(this, msg), monitor)) {
PdbUtil.clearComponents(composite);
}
// Do not adjust size of defined structure contains flex array at specified offset
boolean hasFlexibleArray = false;
if (composite instanceof Structure) {
hasFlexibleArray = ((Structure) composite).hasFlexibleArrayComponent();
}
// // Do not adjust size of defined structure contains flex array at specified offset
// boolean hasFlexibleArray = false;
// if (composite instanceof Structure) {
// hasFlexibleArray = ((Structure) composite).hasFlexibleArrayComponent();
// }
if (!isClasses && !hasFlexibleArray) {
PdbUtil.ensureSize(length, composite, log);
}
// FIXME: This should be handled during finalization of composite layout
// if (!isClass && !hasFlexibleArray) {
// PdbUtil.ensureSize(length, composite, log);
// }
}
}
/**
* check to see if this data type is actually a class
*/
private boolean isDataTypeClass(XmlTreeNode node, TaskMonitor monitor)
throws CancelledException {
if (!node.getStartElement().getName().equals("datatype")) {
return false;
private List<PdbXmlMember> getNormalMembersOnly(CompositeDefinition compositeDefinition) {
if (compositeDefinition.hasNormalMembersOnly) {
return compositeDefinition.memberList;
}
for (int i = 0; i < node.getChildCount(); ++i) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
XmlTreeNode childNode = node.getChildAt(i);
XmlElement child = childNode.getStartElement();
String datatype = child.getAttribute("datatype");
if ("Function".equals(datatype)) {
return true;
ArrayList<PdbXmlMember> list = new ArrayList<>();
for (PdbXmlMember m : compositeDefinition.memberList) {
if (m.kind == PdbXmlKind.MEMBER) {
list.add(m);
}
}
return false;
return list;
}
private void preProcessDataTypeList(XmlPullParser xmlParser, TaskMonitor monitor)
void preProcessDataTypeList(XmlPullParser xmlParser, boolean isClasses, TaskMonitor monitor)
throws SAXParseException, CancelledException {
monitor.setMessage("Pre-processing PDB datatypes...");
String elementType = isClasses ? "classes" : "datatypes";
Map<String, XmlTreeNode> todoNames = new HashMap<>();
while (xmlParser.hasNext()) {
monitor.checkCanceled();
XmlElement elem = xmlParser.peek();
@ -144,50 +166,106 @@ public class ApplyDataTypes {
xmlParser.next();
break;
}
String name = SymbolUtilities.replaceInvalidChars(elem.getAttribute("name"), false);
XmlTreeNode node = new XmlTreeNode(xmlParser);
if (todoNames.containsKey(name)) {
XmlTreeNode todoNode = todoNames.get(name);
if (elem.toString().equals(todoNode.getStartElement().toString())) {
//TODO log.appendMsg("Duplicate data type defined in PDB: "+name);
}
else {
//TODO log.appendMsg("Data type re-definition ignored: "+name);
}
}
else {
if (isClasses || isDataTypeClass(node, monitor)) {
pdbParser.predefineClass(name);
}
todoNames.put(name, node);
String kind = isClasses ? PdbParserNEW.STRUCTURE_KIND : elem.getAttribute("kind");
CompositeDefinition compositeDefinition = new CompositeDefinition(xmlParser);
if (pdbParser.getCachedDataType(name) != null) {
log.appendMsg(
"Error: Data type name collision - unable to define " + kind + ": " + name);
if (!compositeQueue.containsKey(compositeDefinition.name)) {
// could be problematic if duplicate names represent two different composites
if (compositeDefinition.isClass) {
pdbParser.predefineClass(compositeDefinition.name);
}
compositeQueue.put(compositeDefinition.name, compositeDefinition);
if (pdbParser.getCachedDataType(compositeDefinition.name) != null) {
log.appendMsg("Error: Data type name collision - unable to define " +
compositeDefinition.kind.getCamelName() + ": " + compositeDefinition.name);
continue;
}
todo.add(node);
// /** Can this be avoided if using dependency ordering ??
// NOTE: currently composite may grow if zero-length array used
// since we must currently allocate one element since 0-length array
// not yet supported.
Composite composite = pdbParser.createComposite(kind, name);
Composite composite =
pdbParser.createComposite(compositeDefinition.kind, compositeDefinition.name);
if (composite == null) {
log.appendMsg("Unsupported datatype kind (" + kind + "): " + name);
log.appendMsg("Unsupported datatype kind (" + compositeDefinition.kind + "): " +
compositeDefinition.name);
continue;
}
if (!isClasses) {
int length = XmlUtilities.parseInt(elem.getAttribute("length"));
PdbUtil.ensureSize(length, composite, log);
}
pdbParser.cacheDataType(name, composite);
// if (!isClasses) {
// int length = XmlUtilities.parseInt(elem.getAttribute("length"));
// PdbUtil.ensureSize(length, composite, log);
// }
pdbParser.cacheDataType(compositeDefinition.name, composite);
// **/
}
}
todoNames.clear();//release memory...
todoNames = null;
}
private class CompositeDefinition {
final boolean isClass;
final PdbXmlKind kind;
final String name;
final int length;
final List<PdbXmlMember> memberList = new ArrayList<>();
final boolean hasNormalMembersOnly;
CompositeDefinition(XmlPullParser parser) {
XmlElement startElement = parser.start();
name = SymbolUtilities.replaceInvalidChars(startElement.getAttribute("name"), false);
length = XmlUtilities.parseInt(startElement.getAttribute("length"));
String kindStr = startElement.getAttribute("kind");
boolean membersOnly = true;
XmlElement element = parser.peek();
while (element != null && element.isStart()) {
element = parser.start("member");
PdbXmlMember pdbXmlMember = pdbParser.getPdbXmlMember(element);
memberList.add(pdbXmlMember);
membersOnly &= (pdbXmlMember.kind == PdbXmlKind.MEMBER);
parser.end(element);
element = parser.peek();
}
parser.end(startElement);
this.hasNormalMembersOnly = membersOnly;
this.isClass = "class".equals(startElement.getName()) || isInferredClass(kindStr);
this.kind = isClass ? PdbXmlKind.STRUCTURE : PdbXmlKind.parse(kindStr);
}
private boolean isInferredClass(String kindStr) {
for (PdbXmlMember m : memberList) {
if (m.kind == PdbXmlKind.MEMBER) {
continue;
}
if ("void *".equals(m.memberDataTypeName)) {
return true;
}
if ("Function".equals(m.memberDataTypeName)) { // ??
return true;
}
}
return false;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompositeDefinition other = (CompositeDefinition) obj;
return isClass == other.isClass && kind == other.kind && length == other.length &&
SystemUtilities.isEqual(name, other.name);
}
}
}

View file

@ -16,7 +16,7 @@
package ghidra.app.util.bin.format.pdb;
import ghidra.app.cmd.function.CallDepthChangeInfo;
import ghidra.app.util.bin.format.pdb.PdbParserNEW.WrappedDataType;
import ghidra.app.util.bin.format.pdb.PdbParserNEW.PdbXmlMember;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
@ -56,25 +56,25 @@ class ApplyStackVariables {
}
elem = xmlParser.next();//stack variable number start tag
PdbMember member = new PdbMember(elem, monitor);
PdbXmlMember member = pdbParser.getPdbXmlMember(elem);
if ("StaticLocal".equals(member.memberKind)) {
if (PdbXmlKind.STATIC_LOCAL == member.kind) {
xmlParser.next();//stack variable number end tag
continue;
}
DataType dt = getDataType(member, log, monitor);
DataType dt = getDataType(member, log);
if (dt == null) {
continue;
}
if ("ObjectPointer".equals(member.memberKind)) {
if (PdbXmlKind.OBJECT_POINTER == member.kind) {
createRegisterParameter(member.memberName, dt, log);
}
else if ("Parameter".equals(member.memberKind)) {
else if (PdbXmlKind.PARAMETER == member.kind) {
createStackVariable(member.memberName, frameBase + member.memberOffset, dt, log);
}
else if ("Local".equals(member.memberKind)) {
else if (PdbXmlKind.LOCAL == member.kind) {
createStackVariable(member.memberName, frameBase + member.memberOffset, dt, log);
}
@ -188,20 +188,19 @@ class ApplyStackVariables {
return variable;
}
private DataType getDataType(PdbMember member, MessageLog log, TaskMonitor monitor)
throws CancelledException {
WrappedDataType wrappedDataType =
pdbParser.findDataType(member.memberDataTypeName, monitor);
private DataType getDataType(PdbXmlMember member, MessageLog log) throws CancelledException {
WrappedDataType wrappedDataType = pdbParser.findDataType(member.memberDataTypeName);
if (wrappedDataType == null) {
log.appendMsg("Error: failed to resolve data type for " + member.memberKind + ": " +
log.appendMsg("Error: failed to resolve data type for " + member.kind + ": " +
member.memberDataTypeName);
return null;
}
if (wrappedDataType.isZeroLengthArray) {
log.appendMsg("Error: zero length array not supported for for " + member.memberKind +
": " + member.memberDataTypeName);
if (wrappedDataType.isZeroLengthArray()) {
log.appendMsg("Error: zero length array not supported for for " + member.kind + ": " +
member.memberDataTypeName);
return null;
}
return wrappedDataType.dataType;
return wrappedDataType.getDataType();
}
}

View file

@ -20,7 +20,6 @@ import java.util.List;
import org.xml.sax.SAXParseException;
import ghidra.app.util.bin.format.pdb.PdbParserNEW.WrappedDataType;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.data.TypedefDataType;
import ghidra.program.model.symbol.SymbolUtilities;
@ -98,19 +97,20 @@ class ApplyTypeDefs {
continue;//TODO is this actually a global function
}
WrappedDataType baseDataType = pdbParser.findDataType(baseDatatypeName, monitor);
WrappedDataType baseDataType = pdbParser.findDataType(baseDatatypeName);
if (baseDataType == null) {
log.appendMsg("Error: failed to resolve typedef: " + datatypeName + " -> " +
baseDatatypeName);
continue;
}
if (baseDataType.isZeroLengthArray) {
if (baseDataType.isZeroLengthArray()) {
log.appendMsg(
"Error: zero length array not supported for typedef: " + datatypeName);
continue;
}
TypedefDataType typedef = pdbParser.createTypeDef(datatypeName, baseDataType.dataType);
TypedefDataType typedef =
pdbParser.createTypeDef(datatypeName, baseDataType.getDataType());
pdbParser.cacheDataType(datatypeName, typedef); // cache with namespace-based name
}
}

View file

@ -0,0 +1,160 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
import java.util.ArrayList;
import java.util.List;
import ghidra.program.model.data.BitFieldDataType;
import ghidra.program.model.data.DataType;
/**
* <code>BitFieldGroupCompositeMember</code> provides the ability to collect related
* {@link DefaultCompositeMember} members within a group during the composite reconstruction
* process.
*/
public class BitFieldGroupCompositeMember extends CompositeMember {
private List<DefaultCompositeMember> list = new ArrayList<>();
@Override
boolean isBitFieldMember() {
return true;
}
@Override
boolean isSingleBitFieldMember() {
return false;
}
@Override
boolean isContainer() {
return false;
}
@Override
boolean isStructureContainer() {
return false;
}
@Override
boolean isUnionContainer() {
return false;
}
@Override
int getOffset() {
if (list.isEmpty()) {
return 0;
}
return list.get(0).getOffset();
}
int getConsumedBits() {
// TODO: this could be maintained as a field
int consumed = 0;
for (DefaultCompositeMember m : list) {
consumed += ((BitFieldDataType) m.getDataType()).getBitSize();
}
return consumed;
}
@Override
void setOffset(int offset) {
for (DefaultCompositeMember m : list) {
m.setOffset(offset);
}
}
@Override
int getLength() {
if (list.isEmpty()) {
return 0;
}
return list.get(0).getLength();
}
@Override
DefaultCompositeMember getParent() {
if (list.isEmpty()) {
return null;
}
return list.get(0).getParent();
}
@Override
void setParent(DefaultCompositeMember newParent) {
for (DefaultCompositeMember m : list) {
m.setParent(newParent);
}
}
@Override
boolean addMember(DefaultCompositeMember member) {
DataType dt = member.getDataType();
if (dt == null || dt.getLength() <= 0) {
return false;
}
// trigger structure/union transformation
DefaultCompositeMember bf0 = list.remove(0);
return bf0.addMember(member);
}
@Override
boolean addToStructure(DefaultCompositeMember structure) {
// add all bit-fields to structure and allow them to regroup
boolean success = true;
for (DefaultCompositeMember m : list) {
m.setBitFieldGroup(null);
success &= m.addToStructure(structure);
}
return success;
}
@Override
void finalizeDataType(int preferredSize) {
return; // nothing to do
}
private DefaultCompositeMember validateNewMember(CompositeMember member) {
if (!member.isSingleBitFieldMember()) {
throw new IllegalArgumentException("expected single bit-field member");
}
if (!list.isEmpty() &&
(member.getOffset() != getOffset() || member.getLength() != getLength())) {
throw new IllegalArgumentException(
"expected bit-field member with same offset and length");
}
DefaultCompositeMember m = (DefaultCompositeMember) member;
m.setBitFieldGroup(this);
return m;
}
/**
* Add a new member to the end of this bit-field group. The caller should ensure that the
* specified member is a suitable addition to this group (must be single bit field whose
* member offset and length match this group's).
* @param member bit-field member (must have data type of BitFieldDataType).
* @throws IllegalArgumentException if specified member is not suitable for this group.
*/
void addToGroup(CompositeMember member) {
list.add(validateNewMember(member));
}
}

View file

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

View file

@ -0,0 +1,111 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.util.Msg;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
/**
* <code>PdbMember</code> convey PDB member information used for datatype
* reconstruction. The <i>memberDataTypeName</i> is expected to include
* namespace prefixes when relevant. When representing bitfields the
* <i>memberName</i> is used to convey bit-size and bit-offset information
* (e.g., fieldname:SSSS[:XXXX] where SSSS corresponds to the bit-size
* and XXXX corresponds to an optional bit-offset).
*/
public class DefaultPdbMember extends PdbMember {
private final String name;
private boolean isBitField;
private int bitFieldSize = -1;
private int bitFieldOffset = -1;
private final PdbDataTypeParser dataTypeParser;
/**
* Default PDB member construction
* @param name member field name. For bitfields this also conveys the bit-size
* and optionally the bit-offset.
* @param dataTypeName field datatype or the base datatype associated with a bitfield
* @param offset
* @param dataTypeParser
*/
DefaultPdbMember(String name, String dataTypeName, int offset,
PdbDataTypeParser dataTypeParser) {
super(getMemberName(name), dataTypeName, offset);
this.name = name;
parseBitField();
this.dataTypeParser = dataTypeParser;
}
private static String getMemberName(String name) {
int bitFieldColonIndex = name != null ? name.indexOf(':') : -1;
if (bitFieldColonIndex >= 0) {
return name.substring(0, bitFieldColonIndex);
}
return name;
}
@Override
protected WrappedDataType getDataType() throws CancelledException {
WrappedDataType wrappedDt = dataTypeParser.findDataType(getDataTypeName());
if (wrappedDt != null && isBitField) {
if (wrappedDt.isZeroLengthArray()) {
return null;
}
PdbBitField bitFieldDt;
try {
DataType baseDataType =
wrappedDt.getDataType().clone(dataTypeParser.getProgramDataTypeManager());
bitFieldDt = new PdbBitField(baseDataType, bitFieldSize, bitFieldOffset);
}
catch (InvalidDataTypeException e) {
Msg.error(this, "PDB parse error: " + e.getMessage());
return null;
}
wrappedDt = new WrappedDataType(bitFieldDt, false);
}
return wrappedDt;
}
private void parseBitField() {
int bitFieldColonIndex = name != null ? name.indexOf(':') : -1;
if (bitFieldColonIndex >= 0) {
isBitField = true;
String bitSizeOffsetStr = name.substring(bitFieldColonIndex + 1);
try {
int colonIndex = bitSizeOffsetStr.indexOf(':');
if (colonIndex > 0) {
bitFieldOffset = (int) NumericUtilities.parseNumber(
bitSizeOffsetStr.substring(colonIndex + 1));
bitSizeOffsetStr = bitSizeOffsetStr.substring(0, colonIndex);
}
bitFieldSize = (int) NumericUtilities.parseNumber(bitSizeOffsetStr);
}
catch (NumberFormatException e) {
// ignore
}
}
}
}

View file

@ -0,0 +1,83 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
/**
* <code>PdbBitField</code> provides ability to hang onto bitfield as a datatype.
* This will be transformed to a normal BitFieldDataType when cloned.
*/
class PdbBitField extends BitFieldDataType {
private int bitOffsetWithinBaseType;
// TODO: add support for big-endian
/**
* Construct a PDB bitfield (not intended for direct use by DataTypeManager)
* @param baseDataType fielfield base datatype cloned for target datatype manager
* @param bitSize bitfield size in bits
* @param bitOffsetWithinBaseType bit offset within baseDataType or -1 if unknown
* @throws InvalidDataTypeException if invalid bitfield parameters are specified
*/
protected PdbBitField(DataType baseDataType, int bitSize, int bitOffsetWithinBaseType)
throws InvalidDataTypeException {
super(baseDataType, bitSize,
getMinimalBitOffset(baseDataType, bitSize, bitOffsetWithinBaseType), 0);
if (bitSize < 1) {
throw new InvalidDataTypeException("invalid PDB bit size: " + bitSize);
}
if (bitOffsetWithinBaseType < -1) {
throw new InvalidDataTypeException(
"invalid PDB bit offset: " + bitOffsetWithinBaseType);
}
this.bitOffsetWithinBaseType = bitOffsetWithinBaseType;
}
private static int getMinimalBitOffset(DataType baseDataType, int bitSize,
int bitOffsetWithinBaseType) {
if (bitOffsetWithinBaseType < 0) {
return 0;
}
// assumes little endian packing (lsb first)
return bitOffsetWithinBaseType % 8;
}
/**
* Get the bit offset within the full base type
* @return base type bit offset or -1 if unknown
*/
public int getBitOffsetWithinBase() {
return bitOffsetWithinBaseType;
}
@Override
public BitFieldDataType clone(DataTypeManager dtm) {
if (dtm != getDataTypeManager()) {
throw new AssertException("unsupported clone operation");
}
return this;
}
@Override
public String toString() {
return getDisplayName() + "(baseSize:" + getBaseTypeSize() + ",bitOffsetInBase:" +
bitOffsetWithinBaseType + ")";
}
}

View file

@ -0,0 +1,321 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
import java.util.*;
import ghidra.app.services.DataTypeManagerService;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
class PdbDataTypeParser {
private final static String NO_TYPE = "NoType";
private DataTypeManager programDataTypeMgr;
private DataTypeManagerService service;
private TaskMonitor monitor;
private Map<String, DataType> dataTypeCache = new HashMap<>();
PdbDataTypeParser(DataTypeManager programDataTypeMgr, DataTypeManagerService service,
TaskMonitor monitor) {
this.programDataTypeMgr = programDataTypeMgr;
this.service = service;
this.monitor = monitor;
createMandatoryDataTypes();
}
private void createMandatoryDataTypes() {
addDataType(new TypedefDataType("wchar", WideCharDataType.dataType));
addDataType(new TypedefDataType("__int8",
AbstractIntegerDataType.getSignedDataType(1, programDataTypeMgr)));
addDataType(new TypedefDataType("__uint8",
AbstractIntegerDataType.getUnsignedDataType(1, programDataTypeMgr)));
addDataType(new TypedefDataType("__int16",
AbstractIntegerDataType.getSignedDataType(2, programDataTypeMgr)));
addDataType(new TypedefDataType("__uint16",
AbstractIntegerDataType.getUnsignedDataType(2, programDataTypeMgr)));
addDataType(new TypedefDataType("__int32",
AbstractIntegerDataType.getSignedDataType(4, programDataTypeMgr)));
addDataType(new TypedefDataType("__uint32",
AbstractIntegerDataType.getUnsignedDataType(2, programDataTypeMgr)));
addDataType(new TypedefDataType("__int64",
AbstractIntegerDataType.getSignedDataType(8, programDataTypeMgr)));
addDataType(new TypedefDataType("__uint64",
AbstractIntegerDataType.getUnsignedDataType(8, programDataTypeMgr)));
}
/**
* Get the target program's datatype manager
* @return program's datatype manager
*/
public DataTypeManager getProgramDataTypeManager() {
return programDataTypeMgr;
}
void flushDataTypeCache() {
for (DataType dt : dataTypeCache.values()) {
programDataTypeMgr.resolve(dt,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
}
dataTypeCache.clear();
}
/**
* Ensures that the data type managers are used in a particular order.
* The order is as follows:
* 1) the program's data type manager
* 2) the built-in data type manager
* 3) the open data type archives
*/
private class PdbDataTypeManagerComparator implements Comparator<DataTypeManager> {
@Override
public int compare(DataTypeManager dtm1, DataTypeManager dtm2) {
if (dtm1 == programDataTypeMgr) {
return -1;
}
if (dtm2 == programDataTypeMgr) {
return 1;
}
if (dtm1 instanceof BuiltInDataTypeManager) {
return -1;
}
if (dtm2 instanceof BuiltInDataTypeManager) {
return 1;
}
return 0;
}
}
void clear() {
dataTypeCache.clear();
}
DataType getCachedDataType(String key) {
return dataTypeCache.get(key);
}
void cacheDataType(String key, DataType dataType) {
dataTypeCache.put(key, dataType);
}
void addDataType(DataType dataType) {
if (dataType instanceof Composite) {
DataTypeComponent[] components = ((Composite) dataType).getComponents();
for (DataTypeComponent component : components) {
addDataType(component.getDataType());
}
}
ArrayList<DataType> oldDataTypeList = new ArrayList<>();
programDataTypeMgr.findDataTypes(dataType.getName(), oldDataTypeList);
dataType = programDataTypeMgr.addDataType(dataType,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
cacheDataType(dataType.getName(), dataType);
for (DataType oldDataType : oldDataTypeList) {
if (oldDataType.getLength() == 0 &&
oldDataType.getClass().equals(dataType.getClass())) {
try {
programDataTypeMgr.replaceDataType(oldDataType, dataType, false);
}
catch (DataTypeDependencyException e) {
// ignore
}
}
}
}
private DataType findDataTypeInArchives(String datatype, TaskMonitor monitor)
throws CancelledException {
DataTypeManager[] managers = service.getDataTypeManagers();
Arrays.sort(managers, new PdbDataTypeManagerComparator());
for (DataTypeManager manager : managers) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
DataType dt = DataTypeUtilities.findNamespaceQualifiedDataType(manager, datatype, null);
if (dt != null) {
cacheDataType(datatype, dt);
return dt;
}
}
return null;
}
private DataType findBaseDataType(String dataTypeName, TaskMonitor monitor)
throws CancelledException {
DataType dt = getCachedDataType(dataTypeName);
if (dt != null) {
return dt;
}
// PDP category does not apply to built-ins which always live at the root
BuiltInDataTypeManager builtInDTM = BuiltInDataTypeManager.getDataTypeManager();
dt = builtInDTM.getDataType(new DataTypePath(CategoryPath.ROOT, dataTypeName));
if (dt == null) {
dt = findDataTypeInArchives(dataTypeName, monitor);
}
return dt;
}
/**
* Find a data-type by name in a case-sensitive manner.
* @param monitor task monitor
* @param dataTypeName data-type name (may be qualified by its namespace)
* @return wrapped data-type or null if not found.
* @throws CancelledException if operation is cancelled
*/
public WrappedDataType findDataType(String datatype) throws CancelledException {
// NOTE: previous case-insensitive search was removed since type names
// should be case-sensitive
datatype = datatype.trim();
if (datatype == null || datatype.length() == 0) {
return null;
}
if (NO_TYPE.equals(datatype)) {
return new WrappedDataType(VoidDataType.dataType, false); //TODO make it void?
}
String dataTypeName = datatype;
// Example type representations:
// char *[2][3] pointer(array(array(char,3),2))
// char *[2][3] * pointer(array(array(pointer(char),3),2))
// char [0][2][3] * array(array(array(pointer(char),3),2),0)
// char [2][3] * array(array(pointer(char),3),2)
int basePointerDepth = 0;
while (dataTypeName.endsWith("*")) {
++basePointerDepth;
dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim();
}
boolean isZeroLengthArray = false;
List<Integer> arrayDimensions = null;
if (dataTypeName.endsWith("]")) {
arrayDimensions = new ArrayList<>();
dataTypeName = parseArrayDimensions(dataTypeName, arrayDimensions);
if (dataTypeName == null) {
Msg.error(this, "Failed to parse array dimensions: " + datatype);
return null;
}
isZeroLengthArray = (arrayDimensions.get(arrayDimensions.size() - 1) == 0);
}
int pointerDepth = 0;
if (arrayDimensions != null) {
while (dataTypeName.endsWith("*")) {
++pointerDepth;
dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim();
}
if (pointerDepth != 0 && isZeroLengthArray) {
Msg.error(this, "Unsupported pointer to zero-length array: " + datatype);
return null;
}
}
// Find base data-type (name may include namespace, e.g., Foo::MyType)
// Primary cache (dataTypeCache) may contain namespace qualified data type
DataType dt = findBaseDataType(dataTypeName, monitor);
if (dt == null) {
return null; // base type not found
}
while (basePointerDepth-- != 0) {
dt = createPointer(dt);
}
if (arrayDimensions != null) {
dt = createArray(dt, arrayDimensions);
}
while (pointerDepth-- != 0) {
dt = createPointer(dt);
}
return new WrappedDataType(dt, isZeroLengthArray);
}
private String parseArrayDimensions(String datatype, List<Integer> arrayDimensions) {
String dataTypeName = datatype;
boolean zeroLengthArray = false;
while (dataTypeName.endsWith("]")) {
if (zeroLengthArray) {
return null; // only last dimension may be 0
}
int rBracketPos = dataTypeName.lastIndexOf(']');
int lBracketPos = dataTypeName.lastIndexOf('[');
if (lBracketPos < 0) {
return null;
}
int dimension;
try {
dimension = Integer.parseInt(dataTypeName.substring(lBracketPos + 1, rBracketPos));
if (dimension < 0) {
return null; // invalid dimension
}
}
catch (NumberFormatException e) {
return null;
}
dataTypeName = dataTypeName.substring(0, lBracketPos).trim();
arrayDimensions.add(dimension);
}
return dataTypeName;
}
DataType createPointer(DataType dt) {
return PointerDataType.getPointer(dt, programDataTypeMgr);
}
private DataType createArray(DataType dt, List<Integer> arrayDimensions) {
int dimensionCount = arrayDimensions.size();
boolean zeroLengthArray = arrayDimensions.get(arrayDimensions.size() - 1) == 0;
if (zeroLengthArray) {
--dimensionCount;
}
for (int i = 0; i < dimensionCount; i++) {
int dimension = arrayDimensions.get(i);
dt = new ArrayDataType(dt, dimension, dt.getLength(), programDataTypeMgr);
}
if (zeroLengthArray) {
// This should be temporary for supported flex-array cases,
// although we do not really support flex-arrays within unions
// or in the middle of structures.
dt = new ArrayDataType(dt, 1, dt.getLength(), programDataTypeMgr);
}
return dt;
}
}

View file

@ -15,31 +15,59 @@
*/
package ghidra.app.util.bin.format.pdb;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.task.TaskMonitor;
import ghidra.util.xml.XmlUtilities;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlTreeNode;
import ghidra.util.exception.CancelledException;
/**
* <code>PdbMember</code> convey PDB member information used for datatype
* reconstruction.
*/
abstract class PdbMember {
final class PdbMember {
final String memberName;
final String memberDataTypeName;
final int memberOffset;
final String memberKind;
final int memberLength;
PdbMember(XmlTreeNode node, TaskMonitor monitor) {
this(node.getStartElement(), monitor);
protected PdbMember(String memberName, String memberDataTypeName, int memberOffset) {
this.memberName = memberName;
this.memberDataTypeName = memberDataTypeName;
this.memberOffset = memberOffset;
}
PdbMember(XmlElement element, TaskMonitor monitor) {
// TODO: Need to examine consistency of renaming names/data types for space removal across
// all of PDB.
memberName = SymbolUtilities.replaceInvalidChars(element.getAttribute("name"), false);
memberDataTypeName = element.getAttribute("datatype");
memberOffset = XmlUtilities.parseInt(element.getAttribute("offset"));
memberKind = element.getAttribute("kind");
memberLength = XmlUtilities.parseInt(element.getAttribute("length"));
/**
* Get the member's name which will correspond to the field name.
* @return member field name
*/
public String getName() {
return memberName;
}
/**
* Get the member's datatype name (may be namespace qualified)
* @return member's datatype name
*/
public String getDataTypeName() {
return memberDataTypeName;
}
/**
* Get the member's byte offset within the root composite.
* @return member's byte offset
*/
public int getOffset() {
return memberOffset;
}
/**
* Get this member's associated data type which has already been cloned for the
* target program's data type manager. This indicates a dependency callback
* and may be used to trigger resolution for composites. When resolving dependencies
* care must be take to avoid circular dependencies which could occur under certain
* error conditions.
* @param member composite member to be resolved
* @return data-type which corresponds to the specified member's data-type name or null
* if unable to resolve.
* @throws CancelledException if operation cancelled
*/
protected abstract WrappedDataType getDataType() throws CancelledException;
}

View file

@ -30,7 +30,6 @@ import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.*;
import ghidra.framework.options.Options;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
@ -38,9 +37,9 @@ import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.xml.XmlUtilities;
import ghidra.xml.*;
/**
@ -55,8 +54,6 @@ public class PdbParserNEW {
public final static File SPECIAL_PDB_LOCATION = new File("C:/WINDOWS/Symbols");
public final static String PDB_STORAGE_PROPERTY = "PDB Storage Directory";
private final static String NO_TYPE = "NoType";
static final String STRUCTURE_KIND = "Structure";
static final String UNION_KIND = "Union";
@ -72,13 +69,15 @@ public class PdbParserNEW {
}
}
private TaskMonitor monitor;
private final boolean forceAnalysis;
private final File pdbFile;
private final boolean isXML;
private final Program program;
private DataTypeManager dataMgr;
private final DataTypeManagerService service;
private final PdbProgramAttributes programAttributes;
private Process process;
private XmlPullParser parser;
private PdbErrorHandler errHandler;
@ -93,23 +92,24 @@ public class PdbParserNEW {
* by the PDB to be available within the dataTypeCache using namespace-based type
* names.
*/
private Map<String, DataType> dataTypeCache = new HashMap<>();
private PdbDataTypeParser dataTypeParser;
private Map<SymbolPath, Boolean> namespaceMap = new TreeMap<>(); // false: simple namespace, true: class namespace
public PdbParserNEW(File pdbFile, Program program, DataTypeManagerService service,
boolean forceAnalysis) {
this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis);
boolean forceAnalysis, TaskMonitor monitor) {
this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis, monitor);
}
public PdbParserNEW(File pdbFile, Program program, DataTypeManagerService service,
PdbProgramAttributes programAttributes, boolean forceAnalysis) {
PdbProgramAttributes programAttributes, boolean forceAnalysis, TaskMonitor monitor) {
this.pdbFile = pdbFile;
this.categoryPrefix = "/" + pdbFile.getName();
this.pdbCategory = new CategoryPath(categoryPrefix);
this.program = program;
this.dataMgr = program.getDataTypeManager();
this.service = service;
this.forceAnalysis = forceAnalysis;
this.monitor = monitor != null ? monitor : TaskMonitor.DUMMY;
this.isXML = pdbFile.getAbsolutePath().endsWith(PdbFileType.XML.toString());
this.programAttributes = programAttributes;
}
@ -119,7 +119,7 @@ public class PdbParserNEW {
* @return data type manager
*/
DataTypeManager getProgramDataTypeManager() {
return program.getDataTypeManager();
return dataMgr;
}
/**
@ -273,36 +273,33 @@ public class PdbParserNEW {
}
private void completeDefferedTypeParsing(ApplyDataTypes applyDataTypes,
ApplyDataTypes applyClasses, ApplyTypeDefs applyTypeDefs, TaskMonitor monitor,
MessageLog log) throws CancelledException {
ApplyTypeDefs applyTypeDefs, MessageLog log) throws CancelledException {
defineClasses(monitor, log);
if (applyDataTypes != null) {
applyDataTypes.buildDataTypes(monitor);
}
if (applyClasses != null) {
applyClasses.buildDataTypes(monitor);
}
if (applyTypeDefs != null) {
applyTypeDefs.buildTypeDefs(monitor);
applyTypeDefs.buildTypeDefs(monitor); // TODO: no dependencies exit on TypeDefs (use single pass)
}
// Ensure that all data types are resolved
flushDataTypeCache();
if (dataTypeParser != null) {
dataTypeParser.flushDataTypeCache();
}
}
/**
* Apply PDB debug information to the current program
*
* @param monitor Monitor used to cancel processing
* @param log MessageLog used to record errors
* @throws IOException if an error occurs during parsing
* @throws PdbException if PDB file has already been loaded
* @throws CancelledException if user cancels the current action
*/
public void applyTo(TaskMonitor monitor, MessageLog log)
throws IOException, PdbException, CancelledException {
public void applyTo(MessageLog log) throws IOException, PdbException, CancelledException {
if (!parsed) {
throw new IOException("PDB: parse() must be called before applyTo()");
}
@ -316,10 +313,7 @@ public class PdbParserNEW {
Msg.debug(this, "Found PDB for " + program.getName());
try {
PdbUtil.createMandatoryDataTypes(this, monitor);
ApplyDataTypes applyDataTypes = null;
ApplyDataTypes applyClasses = null;
ApplyTypeDefs applyTypeDefs = null;
boolean typesFlushed = false;
@ -351,10 +345,16 @@ public class PdbParserNEW {
ApplyEnums.applyTo(parser, this, monitor, log);
}
else if (element.getName().equals("datatypes")) {
applyDataTypes = new ApplyDataTypes(this, parser, false, monitor, log);
if (applyDataTypes == null) {
applyDataTypes = new ApplyDataTypes(this, log);
}
applyDataTypes.preProcessDataTypeList(parser, false, monitor);
}
else if (element.getName().equals("classes")) {
applyClasses = new ApplyDataTypes(this, parser, true, monitor, log);
if (applyDataTypes == null) {
applyDataTypes = new ApplyDataTypes(this, log);
}
applyDataTypes.preProcessDataTypeList(parser, true, monitor);
}
else if (element.getName().equals("typedefs")) {
applyTypeDefs = new ApplyTypeDefs(this, parser, monitor, log);
@ -362,8 +362,7 @@ public class PdbParserNEW {
else if (element.getName().equals("functions")) {
// apply functions (must occur within XML after all type sections)
if (!typesFlushed) {
completeDefferedTypeParsing(applyDataTypes, applyClasses, applyTypeDefs,
monitor, log);
completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log);
typesFlushed = true;
}
ApplyFunctions.applyTo(this, parser, monitor, log);
@ -371,8 +370,7 @@ public class PdbParserNEW {
else if (element.getName().equals("tables")) {
// apply tables (must occur within XML after all other sections)
if (!typesFlushed) {
completeDefferedTypeParsing(applyDataTypes, applyClasses, applyTypeDefs,
monitor, log);
completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log);
typesFlushed = true;
}
ApplyTables.applyTo(this, parser, monitor, log);
@ -383,8 +381,7 @@ public class PdbParserNEW {
}
if (!typesFlushed) {
completeDefferedTypeParsing(applyDataTypes, applyClasses, applyTypeDefs, monitor,
log);
completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log);
}
Options options = program.getOptions(Program.PROGRAM_INFO);
@ -413,15 +410,6 @@ public class PdbParserNEW {
}
}
private void flushDataTypeCache() {
DataTypeManager dtm = program.getDataTypeManager();
for (DataType dt : dataTypeCache.values()) {
dtm.resolve(dt,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
}
dataTypeCache.clear();
}
void predefineClass(String classname) {
SymbolPath classPath = new SymbolPath(classname);
namespaceMap.put(classPath, true);
@ -701,47 +689,47 @@ public class PdbParserNEW {
}
//errHandler = null;
//thread = null;
dataTypeCache.clear();
if (dataTypeParser != null) {
dataTypeParser.clear();
}
}
boolean isCorrectKind(DataType dt, String kind) {
if (STRUCTURE_KIND.equals(kind)) {
boolean isCorrectKind(DataType dt, PdbXmlKind kind) {
if (kind == PdbXmlKind.STRUCTURE) {
return (dt instanceof Structure);
}
else if (UNION_KIND.equals(kind)) {
else if (kind == PdbXmlKind.UNION) {
return (dt instanceof Union);
}
return false;
}
Composite createComposite(String kind, String name) {
if (STRUCTURE_KIND.equals(kind)) {
Composite createComposite(PdbXmlKind kind, String name) {
if (kind == PdbXmlKind.STRUCTURE) {
return createStructure(name, 0);
}
else if (UNION_KIND.equals(kind)) {
else if (kind == PdbXmlKind.UNION) {
return createUnion(name);
}
return null;
throw new IllegalArgumentException("unsupported kind: " + kind);
}
Structure createStructure(String name, int length) {
return new StructureDataType(getCategory(name, true), stripNamespace(name), length,
program.getDataTypeManager());
dataMgr);
}
Union createUnion(String name) {
return new UnionDataType(getCategory(name, true), stripNamespace(name),
program.getDataTypeManager());
return new UnionDataType(getCategory(name, true), stripNamespace(name), dataMgr);
}
TypedefDataType createTypeDef(String name, DataType baseDataType) {
return new TypedefDataType(getCategory(name, true), stripNamespace(name), baseDataType,
program.getDataTypeManager());
dataMgr);
}
EnumDataType createEnum(String name, int length) {
return new EnumDataType(getCategory(name, true), stripNamespace(name), length,
program.getDataTypeManager());
return new EnumDataType(getCategory(name, true), stripNamespace(name), length, dataMgr);
}
void createString(boolean isUnicode, Address address, MessageLog log, TaskMonitor monitor) {
@ -751,15 +739,15 @@ public class PdbParserNEW {
void createData(Address address, String datatype, MessageLog log, TaskMonitor monitor)
throws CancelledException {
WrappedDataType wrappedDt = findDataType(datatype, monitor);
WrappedDataType wrappedDt = getDataTypeParser().findDataType(datatype);
if (wrappedDt == null) {
log.appendMsg("Error: Failed to resolve datatype " + datatype + " at " + address);
}
else if (wrappedDt.isZeroLengthArray) {
else if (wrappedDt.isZeroLengthArray()) {
Msg.debug(this, "Did not apply zero length array data " + datatype + " at " + address);
}
else {
createData(address, wrappedDt.dataType, log, monitor);
createData(address, wrappedDt.getDataType(), log, monitor);
}
}
@ -818,15 +806,15 @@ public class PdbParserNEW {
return;
}
}
Listing listing = program.getListing();
if (existingData == null) {
try {
program.getListing().clearCodeUnits(address, address.add(dataTypeLength - 1),
false);
listing.clearCodeUnits(address, address.add(dataTypeLength - 1), false);
if (dataType.getLength() == -1) {
program.getListing().createData(address, dataType, dataTypeLength);
listing.createData(address, dataType, dataTypeLength);
}
else {
program.getListing().createData(address, dataType);
listing.createData(address, dataType);
}
}
catch (Exception e) {
@ -836,9 +824,8 @@ public class PdbParserNEW {
}
else if (isDataReplaceable(existingData)) {
try {
program.getListing().clearCodeUnits(address, address.add(dataTypeLength - 1),
false);
program.getListing().createData(address, dataType, dataTypeLength);
listing.clearCodeUnits(address, address.add(dataTypeLength - 1), false);
listing.createData(address, dataType, dataTypeLength);
}
catch (Exception e) {
log.appendMsg("Unable to replace " + dataType.getDisplayName() + " at 0x" +
@ -960,245 +947,17 @@ public class PdbParserNEW {
return false;
}
DataType createPointer(DataType dt) {
return PointerDataType.getPointer(dt, program.getDataTypeManager());
}
// DataType createPointer(DataType dt) {
// return PointerDataType.getPointer(dt, program.getDataTypeManager());
// }
/**
* Find a data-type by name in a case-sensitive manner.
* @param monitor task monitor
* @param dataTypeName data-type name (may be qualified by its namespace)
* @return wrapped data-type or null if not found.
* @throws CancelledException if operation is cancelled
*/
WrappedDataType findDataType(String datatype, TaskMonitor monitor) throws CancelledException {
// NOTE: previous case-insensitive search was removed since type names
// should be case-sensitive
datatype = datatype.trim();
if (datatype == null || datatype.length() == 0) {
return null;
}
if (NO_TYPE.equals(datatype)) {
return new WrappedDataType(VoidDataType.dataType, false); //TODO make it void?
}
String dataTypeName = datatype;
// Example type representations:
// char *[2][3] pointer(array(array(char,3),2))
// char *[2][3] * pointer(array(array(pointer(char),3),2))
// char [0][2][3] * array(array(array(pointer(char),3),2),0)
// char [2][3] * array(array(pointer(char),3),2)
int basePointerDepth = 0;
while (dataTypeName.endsWith("*")) {
++basePointerDepth;
dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim();
}
boolean isZeroLengthArray = false;
List<Integer> arrayDimensions = null;
if (dataTypeName.endsWith("]")) {
arrayDimensions = new ArrayList<>();
dataTypeName = parseArrayDimensions(dataTypeName, arrayDimensions);
if (dataTypeName == null) {
Msg.error(this, "Failed to parse array dimensions: " + datatype);
return null;
}
isZeroLengthArray = (arrayDimensions.get(arrayDimensions.size() - 1) == 0);
}
int pointerDepth = 0;
if (arrayDimensions != null) {
while (dataTypeName.endsWith("*")) {
++pointerDepth;
dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim();
}
if (pointerDepth != 0 && isZeroLengthArray) {
Msg.error(this, "Unsupported pointer to zero-length array: " + datatype);
return null;
}
}
// Find base data-type (name may include namespace, e.g., Foo::MyType)
// Primary cache (dataTypeCache) may contain namespace qualified data type
DataType dt = findBaseDataType(dataTypeName, monitor);
if (dt == null) {
return null; // base type not found
}
while (basePointerDepth-- != 0) {
dt = createPointer(dt);
}
if (arrayDimensions != null) {
dt = createArray(dt, arrayDimensions);
}
while (pointerDepth-- != 0) {
dt = createPointer(dt);
}
return new WrappedDataType(dt, isZeroLengthArray);
}
private DataType findBaseDataType(String dataTypeName, TaskMonitor monitor)
throws CancelledException {
DataType dt = getCachedDataType(dataTypeName);
if (dt != null) {
return dt;
}
// PDP category does not apply to built-ins which always live at the root
BuiltInDataTypeManager builtInDTM = BuiltInDataTypeManager.getDataTypeManager();
dt = builtInDTM.getDataType(new DataTypePath(CategoryPath.ROOT, dataTypeName));
if (dt == null) {
dt = findDataTypeInArchives(dataTypeName, monitor);
}
return dt;
}
static class WrappedDataType {
final boolean isZeroLengthArray;
final DataType dataType;
WrappedDataType(DataType dataType, boolean isZeroLengthArray) {
this.dataType = dataType;
this.isZeroLengthArray = isZeroLengthArray;
}
}
private DataType createArray(DataType dt, List<Integer> arrayDimensions) {
int dimensionCount = arrayDimensions.size();
boolean zeroLengthArray = arrayDimensions.get(arrayDimensions.size() - 1) == 0;
if (zeroLengthArray) {
--dimensionCount;
}
for (int i = 0; i < dimensionCount; i++) {
int dimension = arrayDimensions.get(i);
dt = new ArrayDataType(dt, dimension, dt.getLength(), program.getDataTypeManager());
}
if (zeroLengthArray) {
// TODO: improve support for representing zero-length arrays
dt = new ArrayDataType(dt, 1, dt.getLength(), program.getDataTypeManager());
}
return dt;
}
private String parseArrayDimensions(String datatype, List<Integer> arrayDimensions) {
String dataTypeName = datatype;
boolean zeroLengthArray = false;
while (dataTypeName.endsWith("]")) {
if (zeroLengthArray) {
return null; // only last dimension may be 0
}
int rBracketPos = dataTypeName.lastIndexOf(']');
int lBracketPos = dataTypeName.lastIndexOf('[');
if (lBracketPos < 0) {
return null;
}
int dimension;
try {
dimension = Integer.parseInt(dataTypeName.substring(lBracketPos + 1, rBracketPos));
if (dimension < 0) {
return null; // invalid dimension
}
}
catch (NumberFormatException e) {
return null;
}
dataTypeName = dataTypeName.substring(0, lBracketPos).trim();
arrayDimensions.add(dimension);
}
return dataTypeName;
}
private DataType findDataTypeInArchives(String datatype, TaskMonitor monitor)
throws CancelledException {
DataTypeManager[] managers = service.getDataTypeManagers();
Arrays.sort(managers, new PdbDataTypeManagerComparator());
for (DataTypeManager manager : managers) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
DataType dt = DataTypeUtilities.findNamespaceQualifiedDataType(manager, datatype, null);
if (dt != null) {
cacheDataType(datatype, dt);
return dt;
}
}
return null;
}
/**
* Ensures that the data type managers are used in a particular order.
* The order is as follows:
* 1) the program's data type manager
* 2) the built-in data type manager
* 3) the open data type archives
*/
private class PdbDataTypeManagerComparator implements Comparator<DataTypeManager> {
@Override
public int compare(DataTypeManager dtm1, DataTypeManager dtm2) {
if (dtm1 == program.getDataTypeManager()) {
return -1;
}
if (dtm2 == program.getDataTypeManager()) {
return 1;
}
if (dtm1 instanceof BuiltInDataTypeManager) {
return -1;
}
if (dtm2 instanceof BuiltInDataTypeManager) {
return 1;
}
return 0;
}
}
DataType getCachedDataType(String key) {
return dataTypeCache.get(key);
}
void cacheDataType(String key, DataType dataType) {
dataTypeCache.put(key, dataType);
}
void addDataType(DataType dataType) {
if (dataType instanceof Composite) {
DataTypeComponent[] components = ((Composite) dataType).getComponents();
for (DataTypeComponent component : components) {
addDataType(component.getDataType());
}
}
DataTypeManager dataTypeMgr = program.getDataTypeManager();
ArrayList<DataType> oldDataTypeList = new ArrayList<>();
dataTypeMgr.findDataTypes(dataType.getName(), oldDataTypeList);
dataType = dataTypeMgr.addDataType(dataType,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
cacheDataType(dataType.getName(), dataType);
for (DataType oldDataType : oldDataTypeList) {
if (oldDataType.getLength() == 0 &&
oldDataType.getClass().equals(dataType.getClass())) {
try {
dataTypeMgr.replaceDataType(oldDataType, dataType, false);
}
catch (DataTypeDependencyException e) {
// ignore
}
}
}
}
// DataType getCachedDataType(String key) {
// return dataTypeCache.get(key);
// }
//
// void cacheDataType(String key, DataType dataType) {
// dataTypeCache.put(key, dataType);
// }
/**
* Get the PDB root category path
@ -1540,4 +1299,64 @@ public class PdbParserNEW {
return null;
}
PdbDataTypeParser getDataTypeParser() {
if (program == null) {
throw new AssertException("Parser was not constructed with program");
}
if (dataTypeParser == null) {
dataTypeParser = new PdbDataTypeParser(program.getDataTypeManager(), service, monitor);
}
return dataTypeParser;
}
void cacheDataType(String name, DataType dataType) {
getDataTypeParser().cacheDataType(name, dataType);
}
DataType getCachedDataType(String name) {
return getDataTypeParser().getCachedDataType(name);
}
void addDataType(DataType dataType) {
getDataTypeParser().addDataType(dataType);
}
WrappedDataType findDataType(String dataTypeName) throws CancelledException {
return getDataTypeParser().findDataType(dataTypeName);
}
public PdbMember getPdbXmlMember(XmlTreeNode node) {
return new PdbXmlMember(node);
}
public PdbXmlMember getPdbXmlMember(XmlElement element) {
return new PdbXmlMember(element);
}
class PdbXmlMember extends DefaultPdbMember {
final PdbXmlKind kind;
PdbXmlMember(XmlTreeNode node) {
this(node.getStartElement());
}
PdbXmlMember(XmlElement element) {
super(SymbolUtilities.replaceInvalidChars(element.getAttribute("name"), false),
element.getAttribute("datatype"),
XmlUtilities.parseInt(element.getAttribute("offset")), dataTypeParser);
kind = PdbXmlKind.parse(element.getAttribute("kind"));
}
/**
* Kind of member record. Only those records with a Member kind
* are currently considered for inclusion within a composite.
* @return PDB kind
*/
public PdbXmlKind getKind() {
return kind;
}
}
}

View file

@ -18,13 +18,12 @@ package ghidra.app.util.bin.format.pdb;
import ghidra.app.cmd.comments.SetCommentsCmd;
import ghidra.app.util.PseudoDisassembler;
import ghidra.app.util.PseudoInstruction;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.util.Conv;
import ghidra.util.task.TaskMonitor;
final class PdbUtil {
@ -95,31 +94,31 @@ final class PdbUtil {
return false;
}
final static void ensureSize(int expectedLength, Composite composite, MessageLog log) {
int actualLength = composite.getLength();
if (actualLength < expectedLength) {
composite.setInternallyAligned(false);
if (composite instanceof Structure) {
Structure struct = (Structure) composite;
// if this is an empty structure, the structure will lie to us
// and say it has one element so add 1 to growth factor
struct.growStructure(
expectedLength - actualLength + (struct.isNotYetDefined() ? 1 : 0));
}
// must be a union data type
else {
DataType datatype = new ArrayDataType(DataType.DEFAULT, expectedLength,
DataType.DEFAULT.getLength());
composite.add(datatype);
}
}
else if (actualLength > expectedLength) {
log.appendMsg("Warning: Composite data type generated from PDB has size mismatch. " +
composite.getName() + ": expected 0x" + Integer.toHexString(expectedLength) +
", but was 0x" + Integer.toHexString(actualLength));
}
}
// final static void ensureSize(int expectedLength, Composite composite, MessageLog log) {
// int actualLength = composite.getLength();
// if (actualLength < expectedLength) {
//
// composite.setInternallyAligned(false);
// if (composite instanceof Structure) {
// Structure struct = (Structure) composite;
// // if this is an empty structure, the structure will lie to us
// // and say it has one element so add 1 to growth factor
// struct.growStructure(
// expectedLength - actualLength + (struct.isNotYetDefined() ? 1 : 0));
// }
// // must be a union data type
// else {
// DataType datatype = new ArrayDataType(DataType.DEFAULT, expectedLength,
// DataType.DEFAULT.getLength());
// composite.add(datatype);
// }
// }
// else if (actualLength > expectedLength) {
// log.appendMsg("Warning: Composite data type generated from PDB has size mismatch. " +
// composite.getName() + ": expected 0x" + Integer.toHexString(expectedLength) +
// ", but was 0x" + Integer.toHexString(actualLength));
// }
// }
final static void clearComponents(Composite composite) {
if (composite instanceof Structure) {
@ -160,31 +159,4 @@ final class PdbUtil {
return pass + "th pass";
}
final static void createMandatoryDataTypes(PdbParserNEW parser, TaskMonitor monitor) {
DataTypeManager dtm = parser.getProgramDataTypeManager();
parser.addDataType(new TypedefDataType("wchar", WideCharDataType.dataType));
parser.addDataType(
new TypedefDataType("__int8", AbstractIntegerDataType.getSignedDataType(1, dtm)));
parser.addDataType(
new TypedefDataType("__uint8", AbstractIntegerDataType.getUnsignedDataType(1, dtm)));
parser.addDataType(
new TypedefDataType("__int16", AbstractIntegerDataType.getSignedDataType(2, dtm)));
parser.addDataType(
new TypedefDataType("__uint16", AbstractIntegerDataType.getUnsignedDataType(2, dtm)));
parser.addDataType(
new TypedefDataType("__int32", AbstractIntegerDataType.getSignedDataType(4, dtm)));
parser.addDataType(
new TypedefDataType("__uint32", AbstractIntegerDataType.getUnsignedDataType(2, dtm)));
parser.addDataType(
new TypedefDataType("__int64", AbstractIntegerDataType.getSignedDataType(8, dtm)));
parser.addDataType(
new TypedefDataType("__uint64", AbstractIntegerDataType.getUnsignedDataType(8, dtm)));
}
}

View file

@ -0,0 +1,78 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
public enum PdbXmlKind {
//@formatter:off
STRUCTURE,
UNION,
MEMBER,
STATIC_LOCAL,
OBJECT_POINTER,
PARAMETER,
LOCAL,
UNKNOWN;
//@formatter:on
private final String camelName;
private PdbXmlKind() {
camelName = toCamel(name());
}
/**
* Get the name in camel form
* @return name in camel form
*/
public String getCamelName() {
return camelName;
}
private static String toCamel(String name) {
StringBuilder buf = new StringBuilder();
boolean makeUpper = true;
for (char c : name.toCharArray()) {
if (c == '_') {
makeUpper = true;
continue;
}
if (makeUpper) {
c = Character.toUpperCase(c);
}
buf.append(c);
}
return buf.toString();
}
/**
* Parse case-insensitive kind string and return corresponding PdbKind.
* It is expected that kind strings will be camel notation (e.g., OBJECT_POINTER
* kind string would be ObjectPointer).
* If not identified UNKNOWN will be returned.
* @param kind kind string (underscores not permitted)
* @return PdbKind
*/
public static PdbXmlKind parse(String kind) {
for (PdbXmlKind pdbKind : values()) {
if (pdbKind.camelName.equalsIgnoreCase(kind)) {
return pdbKind;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,60 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
import ghidra.program.model.data.DataType;
/**
* <code>WrappedDataType</code> provide the ability to wrap
* a {@link DataType} with additional information not conveyed
* by the datatype on its' own.
* <P>
* Note that a BitFieldDataType instance may be specified as the datatype
* in order to convey bitfield related information.
*/
public class WrappedDataType {
private final boolean isZeroLengthArray;
private final DataType dataType;
/**
* Constructed wrapped datatype
* @param dataType datatype
* @param isZeroLengthArray true if datatype corresponds to a zero-length
* array which can not directly be represented as an Array datatype,
* else false for all other cases.
*/
protected WrappedDataType(DataType dataType, boolean isZeroLengthArray) {
this.dataType = dataType;
this.isZeroLengthArray = isZeroLengthArray;
}
/**
* @return datatype
*/
public DataType getDataType() {
return dataType;
}
/**
* @return true if datatype corresponds to a zero-length array
* which can not directly be represented as an Array datatype,
* else false for all other cases.
*/
public boolean isZeroLengthArray() {
return isZeroLengthArray;
}
}

View file

@ -15,11 +15,13 @@
*/
package pdb;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import docking.DockingWindowManager;
import docking.widgets.dialogs.MultiLineMessageDialog;
import ghidra.app.plugin.core.analysis.*;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.bin.format.pdb.PdbException;
import ghidra.app.util.bin.format.pdb.PdbParserNEW;
import ghidra.app.util.importer.MessageLog;
@ -32,13 +34,15 @@ import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
class LoadPdbTask extends Task {
private PdbParserNEW parser;
private File pdbFile;
private DataTypeManagerService service;
private final Program program;
LoadPdbTask(Program program, PdbParserNEW parser) {
LoadPdbTask(Program program, File pdbFile, DataTypeManagerService service) {
super("Loading PDB...", true, false, false);
this.program = program;
this.parser = parser;
this.pdbFile = pdbFile;
this.service = service;
}
@Override
@ -56,9 +60,12 @@ class LoadPdbTask extends Task {
public boolean analysisWorkerCallback(Program currentProgram, Object workerContext,
TaskMonitor currentMonitor) throws Exception, CancelledException, PdbException {
PdbParserNEW parser =
new PdbParserNEW(pdbFile, program, service, true, currentMonitor);
parser.parse();
parser.openDataTypeArchives();
parser.applyTo(currentMonitor, log);
parser.applyTo(log);
analyzeSymbols(currentMonitor, log);
return !monitor.isCancelled();

View file

@ -103,10 +103,7 @@ public class PdbPlugin extends Plugin {
return;
}
// Note: use other constructor if we want to enforce Auto-Analysis before
// applying PDB.
final PdbParserNEW parser = new PdbParserNEW(pdb, program, service, true);
TaskLauncher.launch(new LoadPdbTask(program, parser));
TaskLauncher.launch(new LoadPdbTask(program, pdb, service));
}
catch (Exception pe) {
Msg.showError(getClass(), null, "Error", pe.getMessage());

View file

@ -773,11 +773,7 @@ public class PdbSymbolServerPlugin extends Plugin {
return;
}
// Note: use other constructor if we want to enforce Auto-Analysis before
// applying PDB.
final PdbParserNEW parser =
new PdbParserNEW(downloadedPdb, currentProgram, service, true);
TaskLauncher.launch(new LoadPdbTask(currentProgram, parser));
TaskLauncher.launch(new LoadPdbTask(currentProgram, downloadedPdb, service));
}
catch (Exception pe) {
Msg.showError(getClass(), null, "Error", pe.getMessage());

View file

@ -195,12 +195,15 @@ std::wstring getName(IDiaSymbol& symbol) {
DWORD locType = 0;
ULONGLONG len = 0;
DWORD bitPos = 0;
if (symbol.get_locationType(&locType) == S_OK &&
locType == LocIsBitField &&
symbol.get_length(&len) == S_OK) {
const size_t length = wstrName.length() + 4 + 32; // length of: name + ":0x\0" + wag_hex_numeric_str_len
symbol.get_length(&len) == S_OK &&
symbol.get_bitPosition(&bitPos) == S_OK) {
// allocate length of: name + ":0x" + len + ":0x" + bitPos + "\0"
const size_t length = wstrName.length() + 70;
std::vector<wchar_t> str(length);
swprintf_s(str.data(), length, L"%ws:0x%I64x", wstrName.c_str(), len);
swprintf_s(str.data(), length, L"%ws:0x%I64x:0x%x", wstrName.c_str(), len, bitPos);
return escapeXmlEntities(str.data());
}

View file

@ -0,0 +1,783 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.pdb;
import static org.junit.Assert.assertTrue;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Before;
import org.junit.Test;
import ghidra.program.model.data.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest
implements Consumer<String> {
DataTypeManager dataMgr;
PdbDataTypeParser dataTypeParser;
@Before
public void setUp() {
// DataOrganization based on x86-64.cspec
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
dataOrg.setBigEndian(false);
dataOrg.setAbsoluteMaxAlignment(0);
dataOrg.setMachineAlignment(2);
dataOrg.setDefaultPointerAlignment(8);
dataOrg.setPointerSize(8);
dataOrg.setSizeAlignment(8, 8);
BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl();
bitFieldPacking.setUseMSConvention(true);
dataOrg.setBitFieldPacking(bitFieldPacking);
dataMgr = new TestDummyDataTypeManager() {
HashMap<String, DataType> dataTypeMap = new HashMap<>();
@Override
public DataOrganization getDataOrganization() {
return dataOrg;
}
@Override
public DataType addDataType(DataType dataType, DataTypeConflictHandler handler) {
// handler ignored - tests should not induce conflicts
String pathname = dataType.getPathName();
DataType myDt = dataTypeMap.get(pathname);
if (myDt != null) {
return myDt;
}
DataType dt = dataType.clone(this);
dataTypeMap.put(pathname, dt);
return dt;
}
@Override
public DataType findDataType(String dataTypePath) {
return dataTypeMap.get(dataTypePath);
}
@Override
public DataType getDataType(CategoryPath path, String name) {
return super.getDataType(new DataTypePath(path, name).getPath());
}
@Override
public DataType getDataType(String dataTypePath) {
return dataTypeMap.get(dataTypePath);
}
};
dataTypeParser = new PdbDataTypeParser(dataMgr, null, null);
}
@Override
public void accept(String message) {
Msg.info(this, message + " (Test: " + getName() + ")");
}
@Test
public void testSimpleStructureWithBitFields() throws Exception {
StructureDataType struct = new StructureDataType("struct", 0, dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(new MyPdbMember("a", "uchar", 0),
new MyPdbMember("b:0x4", "uchar", 1),
new MyPdbMember("c:0x4", "uchar", 1),
new MyPdbMember("d:0x4", "uchar", 2),
new MyPdbMember("e:0x4", "uint", 4),
new MyPdbMember("f", "ushort", 8));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/struct\n" +
"Aligned\n" +
"Structure struct {\n" +
" 0 uchar 1 a \"\"\n" +
" 1 uchar:4(0) 1 b \"\"\n" +
" 1 uchar:4(4) 1 c \"\"\n" +
" 2 uchar:4(0) 1 d \"\"\n" +
" 4 uint:4(0) 1 e \"\"\n" +
" 8 ushort 2 f \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct);
//@formatter:on
}
@Test
public void testSimpleStructureWithFillerBitFields() throws Exception {
StructureDataType struct = new StructureDataType("struct", 0, dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(new MyPdbMember("a", "uchar", 0),
new MyPdbMember("b:0x2:0x2", "uchar", 1),
new MyPdbMember("c1:0x1:0x4", "uchar", 1),
new MyPdbMember("c2:0x1:0x6", "uchar", 1),
new MyPdbMember("d:0x7:0x0", "uchar", 2),
new MyPdbMember("e:0x4:0x0", "uint", 4),
new MyPdbMember("f", "ushort", 8));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/struct\n" +
"Aligned\n" +
"Structure struct {\n" +
" 0 uchar 1 a \"\"\n" +
" 1 uchar:2(0) 1 padding \"\"\n" +
" 1 uchar:2(2) 1 b \"\"\n" +
" 1 uchar:1(4) 1 c1 \"\"\n" +
" 1 uchar:1(5) 1 padding \"\"\n" +
" 1 uchar:1(6) 1 c2 \"\"\n" +
" 2 uchar:7(0) 1 d \"\"\n" +
" 4 uint:4(0) 1 e \"\"\n" +
" 8 ushort 2 f \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4", struct, true);
//@formatter:on
}
@Test
public void testUnionedStructuresWithFillerBitFields() throws Exception {
/*** Structure Source ***
struct BitFieldUnionAlternatingPadded_s {
union {
struct {
char a0:1;
char :1;
char a2:1;
char :1;
char a4:1;
char :1;
};
struct {
char :1;
char a1:1;
char :1;
char a3:1;
char :1;
char a5:1;
};
};
};
*/
StructureDataType struct = new StructureDataType("struct", 0, dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(
new MyPdbMember("a0:0x1:0x0", "char", 0),
new MyPdbMember("a2:0x1:0x2", "char", 0),
new MyPdbMember("a4:0x1:0x4", "char", 0),
new MyPdbMember("a1:0x1:0x1", "char", 0),
new MyPdbMember("a3:0x1:0x3", "char", 0),
new MyPdbMember("a5:0x1:0x5", "char", 0));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 1, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/struct\n" +
"Aligned\n" +
"Structure struct {\n" +
" 0 struct_u_0 1 null \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/struct/struct_u_0\n" +
"Aligned\n" +
"Union struct_u_0 {\n" +
" 0 struct_u_0_s_0 1 _s_0 \"\"\n" +
" 0 struct_u_0_s_1 1 _s_1 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/struct/struct_u_0/struct_u_0_s_0\n" +
"Aligned\n" +
"Structure struct_u_0_s_0 {\n" +
" 0 char:1(0) 1 a0 \"\"\n" +
" 0 char:1(1) 1 padding \"\"\n" +
" 0 char:1(2) 1 a2 \"\"\n" +
" 0 char:1(3) 1 padding \"\"\n" +
" 0 char:1(4) 1 a4 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/struct/struct_u_0/struct_u_0_s_1\n" +
"Aligned\n" +
"Structure struct_u_0_s_1 {\n" +
" 0 char:1(0) 1 padding \"\"\n" +
" 0 char:1(1) 1 a1 \"\"\n" +
" 0 char:1(2) 1 padding \"\"\n" +
" 0 char:1(3) 1 a3 \"\"\n" +
" 0 char:1(4) 1 padding \"\"\n" +
" 0 char:1(5) 1 a5 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", struct, true);
//@formatter:on
}
@Test
public void testSimpleUnion() throws Exception {
UnionDataType union = new UnionDataType(CategoryPath.ROOT, "union", dataMgr);
// NOTE: preference will be given to packing compatible bitfields at same byte
// offset into structures (currently ambiguous data in PDB xml)
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(new MyPdbMember("a", "uchar", 0),
new MyPdbMember("b:0x4", "uchar", 0),
new MyPdbMember("c:0x4", "uchar", 0),
new MyPdbMember("d:0x4", "uchar", 0),
new MyPdbMember("e:0x4", "uchar", 0),
new MyPdbMember("f", "ushort", 0));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(union, false, 2, members, this,
TaskMonitor.DUMMY));
// NOTE: handling of bit-fields within union is rather ambiguous within
// PDB data
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/union\n" +
"Aligned\n" +
"Union union {\n" +
" 0 uchar 1 a \"\"\n" +
" 0 union_s_1 1 _s_1 \"\"\n" +
" 0 union_s_2 1 _s_2 \"\"\n" +
" 0 ushort 2 f \"\"\n" +
"}\n" +
"Size = 2 Actual Alignment = 2\n" +
"/union/union_s_1\n" +
"Aligned\n" +
"Structure union_s_1 {\n" +
" 0 uchar:4(0) 1 b \"\"\n" +
" 0 uchar:4(4) 1 c \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/union/union_s_2\n" +
"Aligned\n" +
"Structure union_s_2 {\n" +
" 0 uchar:4(0) 1 d \"\"\n" +
" 0 uchar:4(4) 1 e \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1", union, true);
//@formatter:on
}
@Test
public void testComplexStructureWithFlexArray() throws Exception {
UnionDataType struct = new UnionDataType(CategoryPath.ROOT, "union", dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(
new MyPdbMember("a", "int", 0),
new MyPdbMember("b", "int", 4),
new MyPdbMember("c", "int", 8),
new MyPdbMember("d", "char[0]", 12),
new MyPdbMember("e", "longlong", 0),
new MyPdbMember("f", "char[0]", 8));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/union\n" +
"Aligned\n" +
"Union union {\n" +
" 0 union_s_0 12 _s_0 \"\"\n" +
" 0 union_s_1 8 _s_1 \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 8\n" +
"/union/union_s_0\n" +
"Aligned\n" +
"Structure union_s_0 {\n" +
" 0 int 4 a \"\"\n" +
" 4 int 4 b \"\"\n" +
" 8 int 4 c \"\"\n" +
" char[0] 0 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4\n" +
"/union/union_s_1\n" +
"Aligned\n" +
"Structure union_s_1 {\n" +
" 0 longlong 8 e \"\"\n" +
" char[0] 0 f \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8\n", struct, true);
//@formatter:on
}
@Test
public void testComplexStructureWithFlexArray2() throws Exception {
UnionDataType struct = new UnionDataType(CategoryPath.ROOT, "union", dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(
new MyPdbMember("a", "int", 0),
new MyPdbMember("b", "int", 4),
new MyPdbMember("c", "int", 8),
new MyPdbMember("d", "char[0]", 12),
new MyPdbMember("f", "char[0]", 0));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/union\n" +
"Aligned\n" +
"Union union {\n" +
" 0 union_s_0 12 _s_0 \"\"\n" +
" 0 union_s_1 1 _s_1 \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4\n" +
"/union/union_s_0\n" +
"Aligned\n" +
"Structure union_s_0 {\n" +
" 0 int 4 a \"\"\n" +
" 4 int 4 b \"\"\n" +
" 8 int 4 c \"\"\n" +
" char[0] 0 d \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4\n" +
"/union/union_s_1\n" +
"Aligned\n" +
"Structure union_s_1 {\n" +
" char[0] 0 f \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n", struct, true);
//@formatter:on
}
@Test
public void testComplexStructureWithBitFields() throws Exception {
StructureDataType struct = new StructureDataType("struct", 0, dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(
new MyPdbMember("s0:0x1", "char", 0),
new MyPdbMember("s1:0x1", "char", 0),
new MyPdbMember("s2:0x1", "char", 0),
new MyPdbMember("s3:0x1", "char", 0),
new MyPdbMember("s4:0x1", "char", 0),
new MyPdbMember("s5:0x1", "char", 0),
new MyPdbMember("s6:0x1", "char", 0),
new MyPdbMember("s7:0x1", "char", 0),
new MyPdbMember("u0:0x1", "uchar", 0),
new MyPdbMember("u1:0x1", "uchar", 0),
new MyPdbMember("u2:0x1", "uchar", 0),
new MyPdbMember("u3:0x1", "uchar", 0),
new MyPdbMember("u4:0x1", "uchar", 0),
new MyPdbMember("u5:0x1", "uchar", 0),
new MyPdbMember("u6:0x1", "uchar", 0),
new MyPdbMember("u7:0x1", "uchar", 0),
new MyPdbMember("a", "ulong", 8),
new MyPdbMember("b", "longlong", 8));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 16, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/struct\n" +
"Aligned\n" +
"Structure struct {\n" +
" 0 struct_u_0 1 null \"\"\n" +
" 8 struct_u_8 8 null \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8\n" +
"/struct/struct_u_0\n" +
"Aligned\n" +
"Union struct_u_0 {\n" +
" 0 struct_u_0_s_0 1 _s_0 \"\"\n" +
" 0 struct_u_0_s_1 1 _s_1 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/struct/struct_u_0/struct_u_0_s_0\n" +
"Aligned\n" +
"Structure struct_u_0_s_0 {\n" +
" 0 char:1(0) 1 s0 \"\"\n" +
" 0 char:1(1) 1 s1 \"\"\n" +
" 0 char:1(2) 1 s2 \"\"\n" +
" 0 char:1(3) 1 s3 \"\"\n" +
" 0 char:1(4) 1 s4 \"\"\n" +
" 0 char:1(5) 1 s5 \"\"\n" +
" 0 char:1(6) 1 s6 \"\"\n" +
" 0 char:1(7) 1 s7 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/struct/struct_u_0/struct_u_0_s_1\n" +
"Aligned\n" +
"Structure struct_u_0_s_1 {\n" +
" 0 uchar:1(0) 1 u0 \"\"\n" +
" 0 uchar:1(1) 1 u1 \"\"\n" +
" 0 uchar:1(2) 1 u2 \"\"\n" +
" 0 uchar:1(3) 1 u3 \"\"\n" +
" 0 uchar:1(4) 1 u4 \"\"\n" +
" 0 uchar:1(5) 1 u5 \"\"\n" +
" 0 uchar:1(6) 1 u6 \"\"\n" +
" 0 uchar:1(7) 1 u7 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/struct/struct_u_8\n" +
"Aligned\n" +
"Union struct_u_8 {\n" +
" 0 ulong 4 a \"\"\n" +
" 0 longlong 8 b \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8\n", struct, true);
//@formatter:on
}
@Test
public void testComplexStructureWithBitFields2() throws Exception {
UnionDataType struct = new UnionDataType(CategoryPath.ROOT, "union", dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(
new MyPdbMember("a", "ulong", 0),
new MyPdbMember("b", "longlong", 0),
new MyPdbMember("s0:0x1", "char", 0),
new MyPdbMember("s1:0x1", "char", 0),
new MyPdbMember("s2:0x1", "char", 0),
new MyPdbMember("s3:0x1", "char", 0),
new MyPdbMember("s4:0x1", "char", 0),
new MyPdbMember("s5:0x1", "char", 0),
new MyPdbMember("s6:0x1", "char", 0),
new MyPdbMember("s7:0x1", "char", 0),
new MyPdbMember("u0:0x1", "uchar", 1),
new MyPdbMember("u1:0x1", "uchar", 1),
new MyPdbMember("u2:0x1", "uchar", 1),
new MyPdbMember("u3:0x1", "uchar", 1),
new MyPdbMember("u4:0x1", "uchar", 1),
new MyPdbMember("u5:0x1", "uchar", 1),
new MyPdbMember("u6:0x1", "uchar", 1),
new MyPdbMember("u7:0x1", "uchar", 1));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/union\n" +
"Unaligned\n" +
"Union union {\n" +
" 0 ulong 4 a \"\"\n" +
" 0 longlong 8 b \"\"\n" +
" 0 union_s_2 2 _s_2 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 1\n" +
"/union/union_s_2\n" +
"Aligned\n" +
"Structure union_s_2 {\n" +
" 0 char:1(0) 1 s0 \"\"\n" +
" 0 char:1(1) 1 s1 \"\"\n" +
" 0 char:1(2) 1 s2 \"\"\n" +
" 0 char:1(3) 1 s3 \"\"\n" +
" 0 char:1(4) 1 s4 \"\"\n" +
" 0 char:1(5) 1 s5 \"\"\n" +
" 0 char:1(6) 1 s6 \"\"\n" +
" 0 char:1(7) 1 s7 \"\"\n" +
" 1 uchar:1(0) 1 u0 \"\"\n" +
" 1 uchar:1(1) 1 u1 \"\"\n" +
" 1 uchar:1(2) 1 u2 \"\"\n" +
" 1 uchar:1(3) 1 u3 \"\"\n" +
" 1 uchar:1(4) 1 u4 \"\"\n" +
" 1 uchar:1(5) 1 u5 \"\"\n" +
" 1 uchar:1(6) 1 u6 \"\"\n" +
" 1 uchar:1(7) 1 u7 \"\"\n" +
"}\n" +
"Size = 2 Actual Alignment = 1", struct, true);
//@formatter:on
}
// @Ignore defined structure is too ambiguous
@Test
public void testMoreComplicatedStructure() throws Exception {
StructureDataType struct = new StructureDataType("MoreComplicated_s", 0, dataMgr);
//@formatter:off
List<MyPdbMember> members =
CollectionUtils.asList(new MyPdbMember("s0:0x1", "char", 0x0),
new MyPdbMember("s1:0x1", "char", 0x0),
new MyPdbMember("s2:0x1", "char", 0x0),
new MyPdbMember("s3:0x1", "char", 0x0),
new MyPdbMember("s4:0x1", "char", 0x0),
new MyPdbMember("s5:0x1", "char", 0x0),
new MyPdbMember("s6:0x1", "char", 0x0),
new MyPdbMember("s7:0x1", "char", 0x0),
new MyPdbMember("u0:0x1", "uchar", 0x0),
new MyPdbMember("u1:0x1", "uchar", 0x0),
new MyPdbMember("u2:0x1", "uchar", 0x0),
new MyPdbMember("u3:0x1", "uchar", 0x0),
new MyPdbMember("u4:0x1", "uchar", 0x0),
new MyPdbMember("u5:0x1", "uchar", 0x0),
new MyPdbMember("u6:0x1", "uchar", 0x0),
new MyPdbMember("u7:0x1", "uchar", 0x0),
new MyPdbMember("val", "ulong", 0x8),
new MyPdbMember("d", "double", 0x8),
new MyPdbMember("n0:0x4", "ulong", 0x8),
new MyPdbMember("n1:0x4", "ulong", 0x8),
new MyPdbMember("n2:0x4", "ulong", 0x8),
new MyPdbMember("n3:0x4", "ulong", 0x8),
new MyPdbMember("n4:0x4", "ulong", 0x8),
new MyPdbMember("n5:0x4", "ulong", 0x8),
new MyPdbMember("n6:0x4", "ulong", 0x8),
new MyPdbMember("n7:0x4", "ulong", 0x8),
new MyPdbMember("n8:0x4", "ulong", 0xc),
new MyPdbMember("n9:0x4", "ulong", 0xc),
new MyPdbMember("n10:0x4", "ulong", 0xc),
new MyPdbMember("n11:0x4", "ulong", 0xc),
new MyPdbMember("n12:0x4", "ulong", 0xc),
new MyPdbMember("n13:0x4", "ulong", 0xc),
new MyPdbMember("n14:0x4", "ulong", 0xc),
new MyPdbMember("n15:0x4", "ulong", 0xc),
new MyPdbMember("x1:0x1", "ulong", 0x8),
new MyPdbMember("x2:0x2", "ulong", 0x8),
new MyPdbMember("x3:0x3", "ulong", 0x8),
new MyPdbMember("x4:0x4", "ulong", 0x8),
new MyPdbMember("x5:0x5", "ulong", 0x8),
new MyPdbMember("x6:0x6", "ulong", 0x8),
new MyPdbMember("x7:0x7", "ulong", 0x8),
new MyPdbMember("x8:0x8", "ulong", 0xc),
new MyPdbMember("x9:0x9", "ulong", 0xc),
new MyPdbMember("x10:0xa", "ulong", 0xc),
new MyPdbMember("y1:0x1", "uchar", 0x8),
new MyPdbMember("y2:0x2", "uchar", 0x8),
new MyPdbMember("y3:0x3", "uchar", 0x8),
new MyPdbMember("y4:0x4", "uchar", 0x9),
new MyPdbMember("y5:0x5", "uchar", 0xa),
new MyPdbMember("y6:0x6", "uchar", 0xb),
new MyPdbMember("y7:0x7", "uchar", 0xc),
new MyPdbMember("y8:0x8", "uchar", 0xd),
new MyPdbMember("y9:0x9", "ushort", 0xe),
new MyPdbMember("da", "double", 0x10),
new MyPdbMember("ca", "char[8]", 0x18),
new MyPdbMember("cb", "char[8]", 0x10),
new MyPdbMember("db", "double", 0x18),
new MyPdbMember("beef", "char[10]", 0x20),
new MyPdbMember("fromAddress", "int", 0x2c),
new MyPdbMember("toAddress", "int", 0x30),
new MyPdbMember("seqNum", "int", 0x34),
new MyPdbMember("data", "char[0]", 0x38),
new MyPdbMember("buf", "char[0]", 0x2c));
//@formatter:on
assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 0x38, members, this,
TaskMonitor.DUMMY));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this,
"/MoreComplicated_s\n" +
"Aligned\n" +
"Structure MoreComplicated_s {\n" +
" 0 MoreComplicated_s_u_0 1 null \"\"\n" +
" 8 MoreComplicated_s_u_8 8 null \"\"\n" +
" 16 MoreComplicated_s_u_16 16 null \"\"\n" +
" 32 char[10] 10 beef \"\"\n" +
" 44 MoreComplicated_s_u_44 12 null \"\"\n" +
"}\n" +
"Size = 56 Actual Alignment = 8\n" +
"/MoreComplicated_s/MoreComplicated_s_u_0\n" +
"Aligned\n" +
"Union MoreComplicated_s_u_0 {\n" +
" 0 MoreComplicated_s_u_0_s_0 1 _s_0 \"\"\n" +
" 0 MoreComplicated_s_u_0_s_1 1 _s_1 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/MoreComplicated_s/MoreComplicated_s_u_0/MoreComplicated_s_u_0_s_0\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_0_s_0 {\n" +
" 0 char:1(0) 1 s0 \"\"\n" +
" 0 char:1(1) 1 s1 \"\"\n" +
" 0 char:1(2) 1 s2 \"\"\n" +
" 0 char:1(3) 1 s3 \"\"\n" +
" 0 char:1(4) 1 s4 \"\"\n" +
" 0 char:1(5) 1 s5 \"\"\n" +
" 0 char:1(6) 1 s6 \"\"\n" +
" 0 char:1(7) 1 s7 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/MoreComplicated_s/MoreComplicated_s_u_0/MoreComplicated_s_u_0_s_1\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_0_s_1 {\n" +
" 0 uchar:1(0) 1 u0 \"\"\n" +
" 0 uchar:1(1) 1 u1 \"\"\n" +
" 0 uchar:1(2) 1 u2 \"\"\n" +
" 0 uchar:1(3) 1 u3 \"\"\n" +
" 0 uchar:1(4) 1 u4 \"\"\n" +
" 0 uchar:1(5) 1 u5 \"\"\n" +
" 0 uchar:1(6) 1 u6 \"\"\n" +
" 0 uchar:1(7) 1 u7 \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/MoreComplicated_s/MoreComplicated_s_u_16\n" +
"Aligned\n" +
"Union MoreComplicated_s_u_16 {\n" +
" 0 MoreComplicated_s_u_16_s_0 16 _s_0 \"\"\n" +
" 0 MoreComplicated_s_u_16_s_1 16 _s_1 \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8\n" +
"/MoreComplicated_s/MoreComplicated_s_u_16/MoreComplicated_s_u_16_s_0\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_16_s_0 {\n" +
" 0 double 8 da \"\"\n" +
" 8 char[8] 8 ca \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8\n" +
"/MoreComplicated_s/MoreComplicated_s_u_16/MoreComplicated_s_u_16_s_1\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_16_s_1 {\n" +
" 0 char[8] 8 cb \"\"\n" +
" 8 double 8 db \"\"\n" +
"}\n" +
"Size = 16 Actual Alignment = 8\n" +
"/MoreComplicated_s/MoreComplicated_s_u_44\n" +
"Aligned\n" +
"Union MoreComplicated_s_u_44 {\n" +
" 0 MoreComplicated_s_u_44_s_0 12 _s_0 \"\"\n" +
" 0 MoreComplicated_s_u_44_s_1 1 _s_1 \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4\n" +
"/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_0\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_44_s_0 {\n" +
" 0 int 4 fromAddress \"\"\n" +
" 4 int 4 toAddress \"\"\n" +
" 8 int 4 seqNum \"\"\n" +
" char[0] 0 data \"\"\n" +
"}\n" +
"Size = 12 Actual Alignment = 4\n" +
"/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_1\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_44_s_1 {\n" +
" char[0] 0 buf \"\"\n" +
"}\n" +
"Size = 1 Actual Alignment = 1\n" +
"/MoreComplicated_s/MoreComplicated_s_u_8\n" +
"Aligned\n" +
"Union MoreComplicated_s_u_8 {\n" +
" 0 ulong 4 val \"\"\n" +
" 0 double 8 d \"\"\n" +
" 0 MoreComplicated_s_u_8_s_2 8 _s_2 \"\"\n" +
" 0 MoreComplicated_s_u_8_s_3 8 _s_3 \"\"\n" +
" 0 MoreComplicated_s_u_8_s_4 8 _s_4 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 8\n" +
"/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_2\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_8_s_2 {\n" +
" 0 ulong:4(0) 1 n0 \"\"\n" +
" 0 ulong:4(4) 1 n1 \"\"\n" +
" 1 ulong:4(0) 1 n2 \"\"\n" +
" 1 ulong:4(4) 1 n3 \"\"\n" +
" 2 ulong:4(0) 1 n4 \"\"\n" +
" 2 ulong:4(4) 1 n5 \"\"\n" +
" 3 ulong:4(0) 1 n6 \"\"\n" +
" 3 ulong:4(4) 1 n7 \"\"\n" +
" 4 ulong:4(0) 1 n8 \"\"\n" +
" 4 ulong:4(4) 1 n9 \"\"\n" +
" 5 ulong:4(0) 1 n10 \"\"\n" +
" 5 ulong:4(4) 1 n11 \"\"\n" +
" 6 ulong:4(0) 1 n12 \"\"\n" +
" 6 ulong:4(4) 1 n13 \"\"\n" +
" 7 ulong:4(0) 1 n14 \"\"\n" +
" 7 ulong:4(4) 1 n15 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4\n" +
"/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_3\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_8_s_3 {\n" +
" 0 ulong:1(0) 1 x1 \"\"\n" +
" 0 ulong:2(1) 1 x2 \"\"\n" +
" 0 ulong:3(3) 1 x3 \"\"\n" +
" 0 ulong:4(6) 2 x4 \"\"\n" +
" 1 ulong:5(2) 1 x5 \"\"\n" +
" 1 ulong:6(7) 2 x6 \"\"\n" +
" 2 ulong:7(5) 2 x7 \"\"\n" +
" 4 ulong:8(0) 1 x8 \"\"\n" +
" 5 ulong:9(0) 2 x9 \"\"\n" +
" 6 ulong:10(1) 2 x10 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 4\n" +
"/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_4\n" +
"Aligned\n" +
"Structure MoreComplicated_s_u_8_s_4 {\n" +
" 0 uchar:1(0) 1 y1 \"\"\n" +
" 0 uchar:2(1) 1 y2 \"\"\n" +
" 0 uchar:3(3) 1 y3 \"\"\n" +
" 1 uchar:4(0) 1 y4 \"\"\n" +
" 2 uchar:5(0) 1 y5 \"\"\n" +
" 3 uchar:6(0) 1 y6 \"\"\n" +
" 4 uchar:7(0) 1 y7 \"\"\n" +
" 5 uchar:8(0) 1 y8 \"\"\n" +
" 6 ushort:9(0) 2 y9 \"\"\n" +
"}\n" +
"Size = 8 Actual Alignment = 2", struct, true);
//@formatter:on
}
class MyPdbMember extends DefaultPdbMember {
protected MyPdbMember(String memberName, String memberDataTypeName, int memberOffset) {
super(memberName, memberDataTypeName, memberOffset, dataTypeParser);
}
}
}

View file

@ -23,7 +23,6 @@ import java.util.List;
import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.bin.format.pdb.PdbParserNEW.PdbFileType;
@ -35,7 +34,8 @@ import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Program;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.util.task.TaskMonitorAdapter;
import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest {
@ -84,7 +84,7 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest {
public void setUp() throws Exception {
// Get temp directory in which to store files
String tempDirPath = AbstractGenericTest.getTestDirectoryPath();
String tempDirPath = getTestDirectoryPath();
tempDir = new File(tempDirPath);
fileLocation = new File(tempDir, exeFolderName);
@ -100,6 +100,9 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest {
@After
public void tearDown() throws Exception {
if (fileLocation != null) {
FileUtilities.deleteDir(fileLocation);
}
if (createdFiles != null) {
deleteCreatedFiles(createdFiles);
}
@ -991,9 +994,8 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest {
}
private void createDirectory(File directory) {
boolean createSuccess = directory.mkdir();
if (!createSuccess) {
directory.mkdir();
if (!directory.isDirectory()) {
fail("Should have created directory: " + directory.getAbsolutePath());
}
}
@ -1063,11 +1065,12 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest {
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(testProgram);
DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService();
PdbParserNEW parser = new PdbParserNEW(pdb, testProgram, dataTypeManagerService, false);
PdbParserNEW parser =
new PdbParserNEW(pdb, testProgram, dataTypeManagerService, false, TaskMonitor.DUMMY);
parser.openDataTypeArchives();
parser.parse();
parser.applyTo(TaskMonitorAdapter.DUMMY_MONITOR, new MessageLog());
parser.applyTo(new MessageLog());
// Now check program to see if the function has been successfully applied
AddressFactory addressFactory = testProgram.getAddressFactory();

View file

@ -34,7 +34,7 @@ public abstract class AbstractFunctionParameterMarkupItemTest extends AbstractVT
//==================================================================================================
protected DataType createByteDataType(ProgramDB program) {
DataTypeManagerDB dataTypeManager = program.getDataManager();
DataTypeManagerDB dataTypeManager = program.getDataTypeManager();
DataType dataType = new ByteDataType(dataTypeManager);
return dataTypeManager.addDataType(dataType, DataTypeConflictHandler.DEFAULT_HANDLER);
}
@ -43,7 +43,7 @@ public abstract class AbstractFunctionParameterMarkupItemTest extends AbstractVT
int transaction = -1;
try {
transaction = program.startTransaction("Test - Create Data Type");
DataTypeManagerDB dataTypeManager = program.getDataManager();
DataTypeManagerDB dataTypeManager = program.getDataTypeManager();
DataType dataType = new IntegerDataType(dataTypeManager);
return dataTypeManager.addDataType(dataType, DataTypeConflictHandler.DEFAULT_HANDLER);
}

View file

@ -390,7 +390,7 @@ public class DBHandle {
/**
* Returns true if transaction is currently active
*/
protected boolean isTransactionActive() {
public boolean isTransactionActive() {
return txStarted;
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +15,7 @@
*/
package ghidra.util.layout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.*;
/**
* LayoutManger for arranging components into exactly two columns.
@ -30,14 +25,16 @@ public class TwoColumnPairLayout implements LayoutManager {
private int columnGap;
private int pairGap;
private int preferredColumnWidth;
/**
* Constructor for PairLayout.
*/
public TwoColumnPairLayout() {
this(0,0,0,0);
this(0, 0, 0, 0);
}
public TwoColumnPairLayout(int verticalGap, int columnGap, int pairGap, int preferredValueColumnWidth) {
public TwoColumnPairLayout(int verticalGap, int columnGap, int pairGap,
int preferredValueColumnWidth) {
super();
this.verticalGap = verticalGap;
this.columnGap = columnGap;
@ -45,39 +42,46 @@ public class TwoColumnPairLayout implements LayoutManager {
this.preferredColumnWidth = preferredValueColumnWidth;
}
/**
* @see LayoutManager#addLayoutComponent(String, Component)
*/
public void addLayoutComponent(String name, Component comp) {}
@Override
public void addLayoutComponent(String name, Component comp) {
}
/**
* @see LayoutManager#removeLayoutComponent(Component)
*/
public void removeLayoutComponent(Component comp) {}
@Override
public void removeLayoutComponent(Component comp) {
}
/**
* @see LayoutManager#preferredLayoutSize(Container)
*/
@Override
public Dimension preferredLayoutSize(Container parent) {
int rowHeight = getPreferredRowHeight(parent);
int[] widths = getPreferredWidths(parent);
int nRows = (parent.getComponentCount() + 3) / 4;
Insets insets = parent.getInsets();
Dimension d = new Dimension(0,0);
int labelWidth = widths[0];
int valueWidth = preferredColumnWidth == 0 ? widths[1] : preferredColumnWidth;
d.width = 2*labelWidth + 2*valueWidth + columnGap + 2*pairGap + insets.left + insets.right;
d.height = rowHeight *nRows + verticalGap * (nRows-1) + insets.top + insets.bottom;
Dimension d = new Dimension(0, 0);
if (preferredColumnWidth > 0) {
widths[1] = widths[3] = preferredColumnWidth;
}
d.width = widths[0] + widths[1] + widths[2] + widths[3] + columnGap + 2 * pairGap +
insets.left + insets.right;
d.height = rowHeight * nRows + verticalGap * (nRows - 1) + insets.top + insets.bottom;
return d;
}
}
/**
* @see LayoutManager#minimumLayoutSize(Container)
*/
@Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
@ -85,6 +89,7 @@ public class TwoColumnPairLayout implements LayoutManager {
/**
* @see LayoutManager#layoutContainer(Container)
*/
@Override
public void layoutContainer(Container parent) {
int rowHeight = getPreferredRowHeight(parent);
int[] widths = getPreferredWidths(parent);
@ -94,16 +99,21 @@ public class TwoColumnPairLayout implements LayoutManager {
int width = d.width - (insets.left + insets.right);
int x = insets.left;
int y = insets.top;
widths[1] = (width - 2*widths[0] - 2*pairGap - columnGap)/2;
int totalLabelWidth = widths[0] + widths[2];
int padding = 2 * pairGap + columnGap;
int totalValueWidth = (width - totalLabelWidth - padding);
widths[1] = (totalValueWidth * widths[1]) / (widths[1] + widths[3]);
widths[3] = totalValueWidth - widths[1];
int n = parent.getComponentCount();
for(int i=0;i<n;i++) {
int index = i % 2;
for (int i = 0; i < n; i++) {
int index = i % 4;
Component c = parent.getComponent(i);
c.setBounds(x,y,widths[index],rowHeight);
c.setBounds(x, y, widths[index], rowHeight);
x += widths[index];
x += (index == 0) ? pairGap : columnGap;
x += (index == 1) ? columnGap : pairGap;
if (i % 4 == 3) {
y += rowHeight + verticalGap;
x = insets.left;
@ -113,26 +123,25 @@ public class TwoColumnPairLayout implements LayoutManager {
int getPreferredRowHeight(Container parent) {
int height = 0;
int n = parent.getComponentCount();
for(int i=0;i<n;i++) {
for (int i = 0; i < n; i++) {
Component c = parent.getComponent(i);
height = Math.max(height, c.getPreferredSize().height);
}
return height;
}
int[] getPreferredWidths(Container parent) {
int[] widths = new int[2];
int[] widths = new int[4];
int n = parent.getComponentCount();
for(int i=0;i<n;i++) {
for (int i = 0; i < n; i++) {
Component c = parent.getComponent(i);
Dimension d = c.getPreferredSize();
int index = i % 2;
int index = i % 4;
widths[index] = Math.max(widths[index], d.width);
}
return widths;
return widths;
}
}

View file

@ -1273,7 +1273,7 @@ public class GraphAlgorithmsTest extends AbstractGraphAlgorithmsTest {
edge(v5, v6);
edge(v7, v3);
List<List<TestV>> circuits = GraphAlgorithms.findCircuits(g, TaskMonitor.DUMMY);
List<List<TestV>> circuits = GraphAlgorithms.findCircuits(g, false, TaskMonitor.DUMMY);
assertEquals(3, circuits.size());
}

View file

@ -71,7 +71,6 @@
<optional>
<element name="long_double_size"><ref name="value_type"/></element>
</optional>
<optional>
<element name="size_alignment_map">
<zeroOrMore>
@ -82,6 +81,24 @@
</zeroOrMore>
</element>
</optional>
<optional>
<element name="bitfield_packing">
<interleave>
<optional>
<!-- boolean value, default: false (MSVC should be true) -->
<element name="use_MS_convention"><ref name="value_type"/></element>
</optional>
<optional>
<!-- boolean value, default: true -->
<element name="type_alignment_enabled"><ref name="value_type"/></element>
</optional>
<optional>
<!-- int value: number of bytes -->
<element name="zero_length_boundary"><ref name="value_type"/></element>
</optional>
</interleave>
</element>
</optional>
</interleave>
</element>
</optional>

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.util;
import java.util.ConcurrentModificationException;
import java.util.*;
import ghidra.docking.settings.Settings;
import ghidra.program.database.ProgramDB;
@ -53,7 +53,7 @@ public class PseudoData extends PseudoCodeUnit implements Data {
this.dataType = dataType;
baseDataType = getBaseDataType(dataType);
if (program instanceof ProgramDB) {
dataMgr = ((ProgramDB) program).getDataManager();
dataMgr = ((ProgramDB) program).getDataTypeManager();
}
}
@ -103,7 +103,7 @@ public class PseudoData extends PseudoCodeUnit implements Data {
}
/**
/**
* @see ghidra.program.model.listing.Data#getComponent(int)
*/
@Override
@ -187,7 +187,7 @@ public class PseudoData extends PseudoCodeUnit implements Data {
}
/**
*
*
* @see ghidra.program.model.listing.CodeUnit#toString()
*/
@Override
@ -384,6 +384,46 @@ public class PseudoData extends PseudoCodeUnit implements Data {
return null;
}
@Override
public List<Data> getComponentsContaining(int offset) {
List<Data> list = new ArrayList<>();
if (offset < 0 || offset >= length) {
return null;
}
if (baseDataType instanceof Array) {
Array array = (Array) baseDataType;
int elementLength = array.getElementLength();
int index = offset / elementLength;
list.add(getComponent(index));
}
else if (baseDataType instanceof Structure) {
Structure struct = (Structure) baseDataType;
DataTypeComponent dtc = struct.getComponentAt(offset);
// Logic handles overlapping bit-fields
while (dtc != null && offset <= (dtc.getOffset() + dtc.getLength() - 1)) {
int ordinal = dtc.getOrdinal();
list.add(getComponent(ordinal++));
dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null;
}
}
else if (baseDataType instanceof DynamicDataType) {
DynamicDataType ddt = (DynamicDataType) baseDataType;
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
if (dtc != null) {
list.add(getComponent(dtc.getOrdinal()));
}
}
else if (baseDataType instanceof Union) {
if (offset == 0) {
for (int i = 0; i < getNumComponents(); i++) {
list.add(getComponent(i));
}
}
}
return list;
}
/**
* @see ghidra.program.model.listing.Data#getComponentIndex()
*/
@ -431,14 +471,14 @@ public class PseudoData extends PseudoCodeUnit implements Data {
// for(int i=0;i<n;i++) {
// retData[i] = getComponent(i);
// }
// }
// }
// else if (baseDataType instanceof Array) {
// Array array = (Array)baseDataType;
// int n = array.getNumElements();
// retData = new Data[n];
// for(int i=0;i<n;i++) {
// retData[i] = getComponent(i);
// }
// }
// }
// else if (baseDataType instanceof DynamicDataType) {
// DynamicDataType ddt = (DynamicDataType)baseDataType;

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,10 @@
*/
package ghidra.program.database;
import java.io.IOException;
import java.util.*;
import db.*;
import ghidra.framework.Application;
import ghidra.framework.data.DomainObjectAdapterDB;
import ghidra.framework.model.*;
@ -31,16 +34,11 @@ import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import java.io.IOException;
import java.util.*;
import db.*;
/**
* Database implementation for Data Type Archive.
*/
public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataTypeArchive,
DataTypeArchiveChangeManager {
public class DataTypeArchiveDB extends DomainObjectAdapterDB
implements DataTypeArchive, DataTypeArchiveChangeManager {
/**
* DB_VERSION should be incremented any time a change is made to the overall
@ -80,8 +78,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
private final static Class<?>[] COL_CLASS = new Class[] { StringField.class };
private final static String[] COL_TYPES = new String[] { "Value" };
private final static Schema SCHEMA = new Schema(0, StringField.class, "Key", COL_CLASS,
COL_TYPES);
private final static Schema SCHEMA =
new Schema(0, StringField.class, "Key", COL_CLASS, COL_TYPES);
private ProjectDataTypeManager dataTypeManager;
@ -98,8 +96,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
* @throws InvalidNameException
* @throws DuplicateNameException
*/
public DataTypeArchiveDB(DomainFolder folder, String name, Object consumer) throws IOException,
DuplicateNameException, InvalidNameException {
public DataTypeArchiveDB(DomainFolder folder, String name, Object consumer)
throws IOException, DuplicateNameException, InvalidNameException {
super(new DBHandle(), name, 500, 1000, consumer);
this.name = name;
@ -118,7 +116,9 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
endTransaction(id, true);
clearUndo(false);
folder.createFile(name, this, TaskMonitorAdapter.DUMMY_MONITOR);
if (folder != null) {
folder.createFile(name, this, TaskMonitorAdapter.DUMMY_MONITOR);
}
success = true;
}
@ -136,7 +136,7 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
}
/**
* Constructs a new DataTypeArchivemDB
* Constructs a new DataTypeArchiveDB
* @param dbh a handle to an open data type archive database.
* @param openMode one of:
* READ_ONLY: the original database will not be modified
@ -207,8 +207,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
private void propertiesRestore() {
Options pl = getOptions(ARCHIVE_INFO);
boolean origChangeState = changed;
pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "4.3",
null, "Version of Ghidra used to create this program.");
pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "4.3", null,
"Version of Ghidra used to create this program.");
pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created");
// registerDefaultPointerSize();
changed = origChangeState;

View file

@ -156,14 +156,15 @@ abstract public class DatabaseObject {
* This method provides a cheap (lock free) way to test if an object is valid. If
* this object is invalid, then the lock will be used to refresh as needed.
* @param lock the lock that will be used if the object needs to be refreshed.
* @return true if object is valid, else false
*/
public void validate(Lock lock) {
public boolean validate(Lock lock) {
if (!deleted && !isInvalid()) {
return;
return true;
}
lock.acquire();
try {
checkIsValid();
return checkIsValid();
}
finally {
lock.release();

View file

@ -595,7 +595,7 @@ class ListingDB implements Listing {
*/
@Override
public DataTypeManager getDataTypeManager() {
return program.getDataManager();
return program.getDataTypeManager();
}
/* (non-Javadoc)

View file

@ -30,7 +30,6 @@ import ghidra.framework.store.LockException;
import ghidra.program.database.bookmark.BookmarkDBManager;
import ghidra.program.database.code.CodeManager;
import ghidra.program.database.code.InstructionDB;
import ghidra.program.database.data.DataTypeManagerDB;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.database.external.ExternalManagerDB;
import ghidra.program.database.function.FunctionManagerDB;
@ -623,13 +622,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
return (TreeManager) managers[TREE_MGR];
}
/**
* Returns the DataManager
*/
public DataTypeManagerDB getDataManager() {
return (DataTypeManagerDB) managers[DATA_MGR];
}
/**
* @see ghidra.program.model.listing.Program#getDataTypeManager()
*/
@ -2657,7 +2649,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
metadata.put("# of Defined Data", "" + listing.getNumDefinedData());
metadata.put("# of Functions", "" + getFunctionManager().getFunctionCount());
metadata.put("# of Symbols", "" + getSymbolTable().getNumSymbols());
metadata.put("# of Data Types", "" + getDataManager().getDataTypeCount(true));
metadata.put("# of Data Types", "" + getDataTypeManager().getDataTypeCount(true));
metadata.put("# of Data Type Categories", "" + getDataTypeManager().getCategoryCount());
Options propList = getOptions(Program.PROGRAM_INFO);

View file

@ -227,7 +227,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
contextMgr = program.getProgramContext();
refManager = program.getReferenceManager();
propertyMapMgr = program.getUsrPropertyManager();
dataManager = program.getDataManager();
dataManager = program.getDataTypeManager();
protoMgr.setProgram(program);
}
@ -1976,6 +1976,10 @@ public class CodeManager implements ErrorHandler, ManagerDB {
DataDB data = null;
try {
if (dataType instanceof BitFieldDataType) {
throw new CodeUnitInsertionException("Bitfields not supported for Data");
}
DataType originalDataType = dataType;
if (dataType instanceof FactoryDataType) {
MemBuffer memBuffer = new MemoryBufferImpl(program.getMemory(), addr);

View file

@ -16,6 +16,7 @@
package ghidra.program.database.code;
import java.util.ArrayList;
import java.util.List;
import db.Record;
import ghidra.docking.settings.Settings;
@ -66,7 +67,7 @@ class DataDB extends CodeUnitDB implements Data {
dataType = DataType.DEFAULT;
}
this.dataType = dataType;
dataMgr = program.getDataManager();
dataMgr = program.getDataTypeManager();
baseDataType = getBaseDataType(dataType);
@ -554,9 +555,6 @@ class DataDB extends CodeUnitDB implements Data {
}
}
/**
* @see ghidra.program.model.listing.Data#getComponentAt(int)
*/
@Override
public Data getComponentAt(int offset) {
lock.acquire();
@ -594,6 +592,56 @@ class DataDB extends CodeUnitDB implements Data {
}
@Override
public List<Data> getComponentsContaining(int offset) {
List<Data> list = new ArrayList<>();
lock.acquire();
try {
checkIsValid();
if (offset < 0 || offset >= length) {
return null;
}
if (baseDataType instanceof Array) {
Array array = (Array) baseDataType;
int elementLength = array.getElementLength();
int index = offset / elementLength;
list.add(getComponent(index));
}
else if (baseDataType instanceof Structure) {
Structure struct = (Structure) baseDataType;
DataTypeComponent dtc = struct.getComponentAt(offset);
// Logic handles overlapping bit-fields
// Include if offset is contains within bounds of component
while (dtc != null && (offset >= dtc.getOffset()) &&
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
int ordinal = dtc.getOrdinal();
list.add(getComponent(ordinal++));
dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null;
}
}
else if (baseDataType instanceof DynamicDataType) {
DynamicDataType ddt = (DynamicDataType) baseDataType;
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
if (dtc != null) {
list.add(getComponent(dtc.getOrdinal()));
}
}
else if (baseDataType instanceof Union) {
if (offset == 0) {
for (int i = 0; i < getNumComponents(); i++) {
list.add(getComponent(i));
}
}
}
return list;
}
finally {
lock.release();
}
}
/**
* @see ghidra.program.model.listing.Data#getComponentIndex()
*/

View file

@ -0,0 +1,251 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.database.data;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
/**
* <code>BitFieldDBDataType</code> extends BitFieldDataType for DataTypeManagerDB use.
* This class provides the ability to generate a datatype ID and reconstruct a bit-field
* datatype from an ID.
*/
class BitFieldDBDataType extends BitFieldDataType {
// Bit Field ID Encoding (expressed as the following 4-bit nibble fields): XXTTTTTTTTBBOOSS
//
// XX - reserved for datatype manager table ID
// TTTTTTTT - TypeDef/Enum ID (32-bits, excludes table ID, applies to resolved TypeDef/Enum only)
// BB - Encoded base type (8-bits, consists of the following bit fields: xttsbbbb)
// x - 1-bit, unused
// t - 2-bit, =0: base type only, =1:TypeDef used, =2: enum used, =3: abstract-int
// s - 1-bit, storage +1
// xxxx - 4-bits, unused
// OO - bit offset (i.e., right-shift factor, relative to packing base type)
// SS - bit field size in bits
private static final int BIT_OFFSET_SHIFT = 8;
private static final int BASE_TYPE_SHIFT = 16;
private static final int DATATYPE_INDEX_SHIFT = 24;
public static final long MAX_DATATYPE_INDEX = 0xffffffffL; // 32-bits
private static final long ID_TO_INDEX_MASK = ~-(1L << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT);
BitFieldDBDataType(DataType baseDataType, int bitSize, int bitOffset, int storageSize,
DataTypeManager dtm) throws InvalidDataTypeException {
// avoid clone of baseDataType during construction
super(baseDataType, bitSize, bitOffset, storageSize);
}
private static enum BaseDatatypeKind {
NONE(0), TYPEDEF(1), ENUM(2), INTEGER(3);
final int id;
BaseDatatypeKind(int id) {
this.id = id;
}
static BaseDatatypeKind getKind(int value) {
for (BaseDatatypeKind kind : values()) {
if (kind.id == value) {
return kind;
}
}
return NONE;
}
}
/**
* Get a generated ID for this bit-field which is suitable for reconstruction
* via the {@link #getBitFieldDataType(long)} method. This ID encodes the base
* datatype (including typedef/enum and packing data),
* bit-size and bit-offset. The upper byte of the ID will always be zero and
* is reserved for use by the DataTypeManager.
* <p>
* The ability to reference base datatypes (e.g., TypeDef, Enum) is currently limited
* (i.e. 32-bit base datatype ID).
* @param bitfieldDt the resolved bitfield datatype whose ID is needed. This must first be
* resolved by a DataTypeManagerDB.
* @return bit-field ID
*/
static final long getId(BitFieldDataType bitfieldDt) {
DataTypeManager dtm = bitfieldDt.getDataTypeManager();
if (!(dtm instanceof DataTypeManagerDB)) {
throw new AssertException("bitfieldDt must first be resolved");
}
BaseDatatypeKind dataTypeKind = BaseDatatypeKind.NONE;
long dataTypeIndex = 0;
DataType baseDataType = bitfieldDt.getBaseDataType();
if (baseDataType instanceof TypeDef) {
dataTypeKind = BaseDatatypeKind.TYPEDEF;
}
else if (baseDataType instanceof Enum) {
dataTypeKind = BaseDatatypeKind.ENUM;
}
else if (baseDataType instanceof AbstractIntegerDataType) {
dataTypeKind = BaseDatatypeKind.INTEGER;
}
if (dataTypeKind != BaseDatatypeKind.NONE) {
dataTypeIndex = getResolvedDataTypeIndex(baseDataType, (DataTypeManagerDB) dtm);
if (dataTypeIndex == DataTypeManager.NULL_DATATYPE_ID) {
Msg.debug(BitFieldDBDataType.class,
"Bit-Field data type not resolved: " + baseDataType.getName());
dataTypeIndex = MAX_DATATYPE_INDEX;
dataTypeKind = BaseDatatypeKind.NONE;
}
else if (dataTypeIndex >= MAX_DATATYPE_INDEX) {
// TypeDef index exceeds 32-bit limit
Msg.debug(BitFieldDBDataType.class,
"Bit-Field data type index out of range: " + baseDataType.getName());
dataTypeIndex = MAX_DATATYPE_INDEX;
dataTypeKind = BaseDatatypeKind.NONE;
}
}
long id = (dataTypeIndex << DATATYPE_INDEX_SHIFT) |
(getBaseTypeEncodedField(bitfieldDt, dataTypeKind) << BASE_TYPE_SHIFT) |
(bitfieldDt.getBitOffset() << BIT_OFFSET_SHIFT) | bitfieldDt.getDeclaredBitSize();
return id;
}
private static final long getBaseTypeEncodedField(BitFieldDataType bitFieldDt,
BaseDatatypeKind dataTypeKind) {
int nominalStorageSize = BitFieldDataType.getMinimumStorageSize(bitFieldDt.getBitSize());
boolean extraStorageUsed = bitFieldDt.getStorageSize() > nominalStorageSize;
return (dataTypeKind.id << 5) | (extraStorageUsed ? 0x10L : 0L);
}
/**
* Get a bit-field datatype instance for a given ID. The upper byte of the ID will be ignored.
* @param id bit-field datatype ID
* @param dtm data type manager
* @return bit-field data type
*/
static final BitFieldDataType getBitFieldDataType(long id, DataTypeManager dtm) {
int bitSize = (int) (id & 0xff); // 8-bits
int bitOffset = (int) ((id >> BIT_OFFSET_SHIFT) & 0xff); // 8-bits
int baseTypeInfo = (int) ((id >> BASE_TYPE_SHIFT) & 0xff); // 8-bit encoded field
BaseDatatypeKind baseDataTypeKind = BaseDatatypeKind.getKind((baseTypeInfo >> 5) & 3);
boolean extraStorageUsed = (baseTypeInfo & 0x10) != 0;
DataType baseDataType = null;
long dataTypeIndex = (id >> DATATYPE_INDEX_SHIFT) & MAX_DATATYPE_INDEX; // 32-bits
if (baseDataTypeKind != BaseDatatypeKind.NONE && dataTypeIndex != MAX_DATATYPE_INDEX) {
if (baseDataTypeKind == BaseDatatypeKind.TYPEDEF) {
baseDataType = getTypeDef(dataTypeIndex, dtm);
}
else if (baseDataTypeKind == BaseDatatypeKind.ENUM) {
baseDataType = getEnum(dataTypeIndex, dtm);
}
else {
baseDataType = getIntegerType(dataTypeIndex, dtm);
}
}
try {
if (baseDataType == null) {
// use integer datatype on failure
baseDataType = IntegerDataType.dataType.clone(dtm);
}
int effectiveBitSize = getEffectiveBitSize(bitSize, baseDataType.getLength());
int storageSize = getMinimumStorageSize(effectiveBitSize);
if (extraStorageUsed) {
++storageSize;
}
return new BitFieldDBDataType(baseDataType, bitSize, bitOffset, storageSize, dtm);
}
catch (InvalidDataTypeException e) {
return null;
}
}
private static final long getResolvedDataTypeIndex(DataType dataType, DataTypeManagerDB dtm) {
long dataTypeId = dtm.getID(dataType);
if (dataTypeId == DataTypeManager.NULL_DATATYPE_ID) {
return DataTypeManager.NULL_DATATYPE_ID;
}
return dataTypeId & ID_TO_INDEX_MASK;
}
/**
* Get the TypeDef which corresponds to the specified typeDefIndex and the
* specified data type manager.
* @param typeDefIndex base data type index used by bit-field
* @param primitiveBaseDataType expected primitive base datatype
* @param dtm data type manager
* @return TypeDef data type or null if not found
*/
private static final TypeDef getTypeDef(long typeDefIndex, DataTypeManager dtm) {
long dataTypeId =
((long) DataTypeManagerDB.TYPEDEF << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT) |
typeDefIndex;
DataType dataType = dtm.getDataType(dataTypeId);
if (!(dataType instanceof TypeDef)) {
return null;
}
TypeDef typeDefDt = (TypeDef) dataType;
DataType dt = typeDefDt.getBaseDataType();
if (dt instanceof Enum) {
// TODO: how restrictive should we be on matching enum size?
return typeDefDt;
}
if (dt instanceof AbstractIntegerDataType) {
return typeDefDt;
}
return null; // unsupported typedef
}
/**
* Get the Enum which corresponds to the specified enumIndex and the
* specified data type manager.
* @param enumIndex enum data type index used by bit-field
* @param dtm data type manager
* @return Enum data type or null if not found
*/
private static final Enum getEnum(long enumIndex, DataTypeManager dtm) {
long dataTypeId =
((long) DataTypeManagerDB.ENUM << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT) | enumIndex;
DataType dataType = dtm.getDataType(dataTypeId);
if (!(dataType instanceof Enum)) {
return null;
}
return (Enum) dataType;
}
/**
* Get the integer base type which corresponds to the specified intTypeIndex and the
* specified data type manager.
* @param intTypeIndex base data type index used by bit-field
* @param dtm data type manager
* @return integer data type or null if not found
*/
private static final AbstractIntegerDataType getIntegerType(long intTypeIndex,
DataTypeManager dtm) {
long dataTypeId =
((long) DataTypeManagerDB.BUILT_IN << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT) |
intTypeIndex;
DataType dataType = dtm.getDataType(dataTypeId);
if (!(dataType instanceof AbstractIntegerDataType)) {
return null;
}
return (AbstractIntegerDataType) dataType;
}
}

Some files were not shown because too many files have changed in this diff Show more