BitFields - corrected bitfield transitions between different data

organizations and packing for unions.
This commit is contained in:
ghidra1 2019-07-10 17:06:56 -04:00
parent 486ce82586
commit f54f68c66e
22 changed files with 352 additions and 315 deletions

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.core.compositeeditor;
import java.awt.Component; import java.awt.Component;
import java.awt.Window; import java.awt.Window;
import javax.swing.JTable;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import docking.ActionContext; import docking.ActionContext;
@ -67,10 +66,7 @@ public class AddBitFieldAction extends CompositeEditorTableAction {
} }
private void refreshTableAndSelection(CompEditorModel editorModel, int ordinal) { private void refreshTableAndSelection(CompEditorModel editorModel, int ordinal) {
editorModel.fireTableDataChanged(); editorModel.notifyCompositeChanged();
editorModel.compositeInfoChanged();
JTable editorTable = provider.getTable();
editorTable.getSelectionModel().setSelectionInterval(ordinal, ordinal);
} }
@Override @Override

View file

@ -20,6 +20,7 @@ import java.awt.event.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import javax.help.UnsupportedOperationException;
import javax.swing.*; import javax.swing.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -382,16 +383,17 @@ public class BitFieldPlacementComponent extends JPanel {
String name = (fieldName != null && fieldName.length() != 0) ? fieldName : null; String name = (fieldName != null && fieldName.length() != 0) ? fieldName : null;
DataTypeComponent dtc; DataTypeComponent dtc;
if (composite instanceof Union) { if (composite instanceof Union) {
dtc = composite.insertBitField(ordinal, allocationByteSize, throw new UnsupportedOperationException(
bitFieldAllocation.bitOffset, baseDataType, bitFieldAllocation.bitSize, name, "Union modification not currently supported");
null); // dtc = composite.insertBitField(ordinal, allocationByteSize,
} // bitFieldAllocation.bitOffset, baseDataType, bitFieldAllocation.bitSize, name,
else { // null);
Structure struct = (Structure) composite;
dtc = struct.insertBitFieldAt(allocationByteOffset, allocationByteSize,
bitFieldAllocation.bitOffset, baseDataType, bitFieldAllocation.bitSize, name,
null);
} }
// else {
Structure struct = (Structure) composite;
dtc = struct.insertBitFieldAt(allocationByteOffset, allocationByteSize,
bitFieldAllocation.bitOffset, baseDataType, bitFieldAllocation.bitSize, name, null);
// }
if (listener != null) { if (listener != null) {
listener.componentChanged(dtc.getOrdinal()); listener.componentChanged(dtc.getOrdinal());
} }
@ -484,9 +486,22 @@ public class BitFieldPlacementComponent extends JPanel {
if (tip == null) { if (tip == null) {
return null; return null;
} }
String conflictMsg = "";
DataTypeComponent conflict = attrs.getConflict();
if (conflict != null) {
if (tip.length() != 0) {
conflictMsg = "<br>";
}
String conflictName = conflict.getFieldName();
String conflictTip = "'" + conflict.getDataType().getDisplayName() +
(conflictName != null ? (" " + conflictName) : "") + "' at offset " +
conflict.getOffset();
conflictMsg += "<div style=\"color: red;font-style: italic\">conflict with " +
HTMLUtilities.escapeHTML(conflictTip) + "</div>";
}
return "<HTML><div style=\"text-align:center\">" + HTMLUtilities.escapeHTML(tip) + return "<HTML><div style=\"text-align:center\">" + HTMLUtilities.escapeHTML(tip) +
conflictMsg +
"<div style=\"color: gray;font-style: italic\">(Shift-wheel to zoom)</div></div></HTML>"; "<div style=\"color: gray;font-style: italic\">(Shift-wheel to zoom)</div></div></HTML>";
} }
@Override @Override
@ -1050,8 +1065,7 @@ public class BitFieldPlacementComponent extends JPanel {
return null; return null;
} }
String name = dtc.getFieldName(); String name = dtc.getFieldName();
return dtc.getDataType().getDisplayName() + return dtc.getDataType().getDisplayName() + (name != null ? (" " + name) : "");
(name != null ? (" " + dtc.getFieldName()) : "");
} }
DataTypeComponent getDataTypeComponent(boolean ignoreActiveComponent) { DataTypeComponent getDataTypeComponent(boolean ignoreActiveComponent) {

View file

@ -147,7 +147,7 @@ public class UnionDataTypeTest extends AbstractGTest {
union.delete(0); union.delete(0);
} }
// NOTE: bitOffset ignored for union // NOTE: bitOffset ignored for union
union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment"); union.insertBitField(0, IntegerDataType.dataType, 2, "bf1", "bf1Comment");
union.insert(0, ShortDataType.dataType); union.insert(0, ShortDataType.dataType);
//@formatter:off //@formatter:off
@ -168,7 +168,7 @@ public class UnionDataTypeTest extends AbstractGTest {
for (int i = 0; i < cnt; i++) { for (int i = 0; i < cnt; i++) {
union.delete(0); union.delete(0);
} }
union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment"); union.insertBitField(0, IntegerDataType.dataType, 2, "bf1", "bf1Comment");
union.insert(0, ShortDataType.dataType); union.insert(0, ShortDataType.dataType);
union.setInternallyAligned(true); union.setInternallyAligned(true);
@ -186,8 +186,8 @@ public class UnionDataTypeTest extends AbstractGTest {
@Test @Test
public void testInsertBitFieldLittleEndian() throws Exception { public void testInsertBitFieldLittleEndian() throws Exception {
union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); union.insertBitField(2, IntegerDataType.dataType, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); union.insertBitField(3, ByteDataType.dataType, 4, "bf2", "bf2Comment");
//@formatter:off //@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
@ -209,8 +209,8 @@ public class UnionDataTypeTest extends AbstractGTest {
transitionToBigEndian(); transitionToBigEndian();
union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); union.insertBitField(2, IntegerDataType.dataType, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); union.insertBitField(3, ByteDataType.dataType, 4, "bf2", "bf2Comment");
//@formatter:off //@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +

View file

@ -42,8 +42,8 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel
CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" +
"Aligned\n" + "Aligned\n" +
"Union U1 {\n" + "Union U1 {\n" +
" 0 int:4(0) 1 a \"\"\n" + " 0 int:4(4) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" + " 0 int:2(6) 1 b \"\"\n" +
"}\n" + "}\n" +
"Size = 4 Actual Alignment = 4", struct); "Size = 4 Actual Alignment = 4", struct);
//@formatter:on //@formatter:on
@ -56,9 +56,9 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel
CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" +
"Aligned\n" + "Aligned\n" +
"Union U1z {\n" + "Union U1z {\n" +
" 0 int:4(0) 1 a \"\"\n" + " 0 int:4(4) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" + // has no impact " 0 longlong:0(7) 1 \"\"\n" + // has no impact
" 0 int:2(0) 1 b \"\"\n" + " 0 int:2(6) 1 b \"\"\n" +
"}\n" + "}\n" +
"Size = 4 Actual Alignment = 4", struct); "Size = 4 Actual Alignment = 4", struct);
//@formatter:on //@formatter:on
@ -71,8 +71,8 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel
CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" +
"Aligned pack(1)\n" + "Aligned pack(1)\n" +
"Union U1p1 {\n" + "Union U1p1 {\n" +
" 0 int:4(0) 1 a \"\"\n" + " 0 int:4(4) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" + " 0 int:2(6) 1 b \"\"\n" +
"}\n" + "}\n" +
"Size = 1 Actual Alignment = 1", struct); "Size = 1 Actual Alignment = 1", struct);
//@formatter:on //@formatter:on
@ -85,9 +85,9 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel
CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" +
"Aligned pack(1)\n" + "Aligned pack(1)\n" +
"Union U1p1z {\n" + "Union U1p1z {\n" +
" 0 int:4(0) 1 a \"\"\n" + " 0 int:4(4) 1 a \"\"\n" +
" 0 longlong:0(0) 1 \"\"\n" + // has no impact " 0 longlong:0(7) 1 \"\"\n" + // has no impact
" 0 int:2(0) 1 b \"\"\n" + " 0 int:2(6) 1 b \"\"\n" +
"}\n" + "}\n" +
"Size = 1 Actual Alignment = 1", struct); "Size = 1 Actual Alignment = 1", struct);
//@formatter:on //@formatter:on
@ -100,8 +100,8 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel
CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" +
"Aligned pack(2)\n" + "Aligned pack(2)\n" +
"Union U1p2 {\n" + "Union U1p2 {\n" +
" 0 int:4(0) 1 a \"\"\n" + " 0 int:4(4) 1 a \"\"\n" +
" 0 int:2(0) 1 b \"\"\n" + " 0 int:2(6) 1 b \"\"\n" +
"}\n" + "}\n" +
"Size = 2 Actual Alignment = 2", struct); "Size = 2 Actual Alignment = 2", struct);
//@formatter:on //@formatter:on

View file

@ -38,7 +38,7 @@ class PdbBitField extends BitFieldDataType {
protected PdbBitField(DataType baseDataType, int bitSize, int bitOffsetWithinBaseType) protected PdbBitField(DataType baseDataType, int bitSize, int bitOffsetWithinBaseType)
throws InvalidDataTypeException { throws InvalidDataTypeException {
super(baseDataType, bitSize, super(baseDataType, bitSize,
getMinimalBitOffset(baseDataType, bitSize, bitOffsetWithinBaseType), 0); getMinimalBitOffset(baseDataType, bitSize, bitOffsetWithinBaseType));
if (bitSize < 1) { if (bitSize < 1) {
throw new InvalidDataTypeException("invalid PDB bit size: " + bitSize); throw new InvalidDataTypeException("invalid PDB bit size: " + bitSize);
} }

View file

@ -34,7 +34,7 @@ class BitFieldDBDataType extends BitFieldDataType {
// BB - Encoded base type (8-bits, consists of the following bit fields: xttsbbbb) // BB - Encoded base type (8-bits, consists of the following bit fields: xttsbbbb)
// x - 1-bit, unused // x - 1-bit, unused
// t - 2-bit, =0: base type only, =1:TypeDef used, =2: enum used, =3: abstract-int // t - 2-bit, =0: base type only, =1:TypeDef used, =2: enum used, =3: abstract-int
// s - 1-bit, storage +1 // s - 1-bit, storage +1 (NOT-USED! - may be re-purposed by future schema change)
// xxxx - 4-bits, unused // xxxx - 4-bits, unused
// OO - bit offset (i.e., right-shift factor, relative to packing base type) // OO - bit offset (i.e., right-shift factor, relative to packing base type)
// SS - bit field size in bits // SS - bit field size in bits
@ -47,10 +47,21 @@ class BitFieldDBDataType extends BitFieldDataType {
private static final long ID_TO_INDEX_MASK = ~-(1L << DataTypeManagerDB.DATA_TYPE_KIND_SHIFT); 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 { * Construct DB resident bitfield. Minimal storage size and effective bit size will
// avoid clone of baseDataType during construction * be computed based upon specified parameters.
super(baseDataType, bitSize, bitOffset, storageSize); * @param baseDataType base data type (integer/enum type or typedef to same). This
* bitfield will adopt the same datatype manager as this base type.
* @param bitSize size of bit-field expressed as number of bits (0..255). The effective
* bit size may be reduced based upon the specified base datatype size.
* @param bitOffset right shift factor within storage unit when viewed as a big-endian dd
* scalar value. Based upon minimal storage bitOffset should be in the range 0 to 7.
* @throws InvalidDataTypeException
*/
BitFieldDBDataType(DataType baseDataType, int bitSize, int bitOffset)
throws InvalidDataTypeException {
// must avoid cloning of baseDataType during construction!
super(baseDataType, bitSize, bitOffset);
} }
private static enum BaseDatatypeKind { private static enum BaseDatatypeKind {
@ -137,14 +148,14 @@ class BitFieldDBDataType extends BitFieldDataType {
* @param dtm data type manager * @param dtm data type manager
* @return bit-field data type * @return bit-field data type
*/ */
static final BitFieldDataType getBitFieldDataType(long id, DataTypeManager dtm) { static final BitFieldDataType getBitFieldDataType(long id, DataTypeManagerDB dtm) {
int bitSize = (int) (id & 0xff); // 8-bits int bitSize = (int) (id & 0xff); // 8-bits
int bitOffset = (int) ((id >> BIT_OFFSET_SHIFT) & 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 int baseTypeInfo = (int) ((id >> BASE_TYPE_SHIFT) & 0xff); // 8-bit encoded field
BaseDatatypeKind baseDataTypeKind = BaseDatatypeKind.getKind((baseTypeInfo >> 5) & 3); BaseDatatypeKind baseDataTypeKind = BaseDatatypeKind.getKind((baseTypeInfo >> 5) & 3);
boolean extraStorageUsed = (baseTypeInfo & 0x10) != 0; // boolean extraStorageUsed = (baseTypeInfo & 0x10) != 0;
DataType baseDataType = null; DataType baseDataType = null;
long dataTypeIndex = (id >> DATATYPE_INDEX_SHIFT) & MAX_DATATYPE_INDEX; // 32-bits long dataTypeIndex = (id >> DATATYPE_INDEX_SHIFT) & MAX_DATATYPE_INDEX; // 32-bits
@ -164,12 +175,12 @@ class BitFieldDBDataType extends BitFieldDataType {
// use integer datatype on failure // use integer datatype on failure
baseDataType = IntegerDataType.dataType.clone(dtm); baseDataType = IntegerDataType.dataType.clone(dtm);
} }
int effectiveBitSize = getEffectiveBitSize(bitSize, baseDataType.getLength()); // int effectiveBitSize = getEffectiveBitSize(bitSize, baseDataType.getLength());
int storageSize = getMinimumStorageSize(effectiveBitSize); // int storageSize = getMinimumStorageSize(effectiveBitSize);
if (extraStorageUsed) { // if (extraStorageUsed) {
++storageSize; // ++storageSize;
} // }
return new BitFieldDBDataType(baseDataType, bitSize, bitOffset, storageSize, dtm); return new BitFieldDBDataType(baseDataType, bitSize, bitOffset);
} }
catch (InvalidDataTypeException e) { catch (InvalidDataTypeException e) {
return null; return null;

View file

@ -104,13 +104,6 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
return record.getLongValue(CompositeDBAdapter.COMPOSITE_CAT_COL); return record.getLongValue(CompositeDBAdapter.COMPOSITE_CAT_COL);
} }
@Override
public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName,
String comment) throws InvalidDataTypeException {
BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0, 0, dataMgr);
return add(bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
}
/** /**
* Handle replacement of datatype which may impact bitfield datatype. * Handle replacement of datatype which may impact bitfield datatype.
* @param bitfieldComponent bitfield component * @param bitfieldComponent bitfield component
@ -140,9 +133,8 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
} }
try { try {
BitFieldDBDataType newBitfieldDt = BitFieldDBDataType newBitfieldDt = new BitFieldDBDataType(newDt,
new BitFieldDBDataType(newDt, bitfieldDt.getDeclaredBitSize(), bitfieldDt.getDeclaredBitSize(), bitfieldDt.getBitOffset());
bitfieldDt.getBitOffset(), bitfieldDt.getStorageSize(), dataMgr);
bitfieldComponent.setDataType(newBitfieldDt); bitfieldComponent.setDataType(newBitfieldDt);
oldDt.removeParent(this); oldDt.removeParent(this);
newDt.addParent(this); newDt.addParent(this);

View file

@ -790,7 +790,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
storageSize = baseLength; storageSize = baseLength;
} }
try { try {
return new BitFieldDBDataType(resolvedBaseDt, bitSize, bitOffset, storageSize, this); return new BitFieldDBDataType(resolvedBaseDt, bitSize, bitOffset);
} }
catch (InvalidDataTypeException e) { catch (InvalidDataTypeException e) {
throw new AssertException("unexpected", e); throw new AssertException("unexpected", e);

View file

@ -276,6 +276,17 @@ class StructureDB extends CompositeDB implements Structure {
return null; return null;
} }
@Override
public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName,
String comment) throws InvalidDataTypeException {
BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(getDataTypeManager());
BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0);
return add(bitFieldDt, componentName, comment);
}
@Override @Override
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment) DataType baseDataType, int bitSize, String componentName, String comment)
@ -298,8 +309,7 @@ class StructureDB extends CompositeDB implements Structure {
} }
// handle aligned bitfield insertion // handle aligned bitfield insertion
BitFieldDataType bitFieldDt = BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0);
new BitFieldDBDataType(baseDataType, bitSize, 0, 0, dataMgr);
return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
} }
finally { finally {
@ -315,7 +325,7 @@ class StructureDB extends CompositeDB implements Structure {
try { try {
checkDeleted(); checkDeleted();
BitFieldDataType.checkBaseDataType(baseDataType); BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(getDataTypeManager()); baseDataType = baseDataType.clone(dataMgr);
if (byteOffset < 0 || bitSize < 0) { if (byteOffset < 0 || bitSize < 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -405,11 +415,8 @@ class StructureDB extends CompositeDB implements Structure {
structLength = requiredLength; structLength = requiredLength;
} }
// use minimal storage // adjust for minimal storage use
int storageBitOffset = bitOffset % 8; int storageBitOffset = bitOffset % 8;
int storageSize =
BitFieldDataType.getMinimumStorageSize(effectiveBitSize + storageBitOffset);
int revisedOffset; int revisedOffset;
if (bigEndian) { if (bigEndian) {
revisedOffset = byteOffset + byteWidth - ((effectiveBitSize + bitOffset + 7) / 8); revisedOffset = byteOffset + byteWidth - ((effectiveBitSize + bitOffset + 7) / 8);
@ -418,11 +425,11 @@ class StructureDB extends CompositeDB implements Structure {
revisedOffset = byteOffset + (bitOffset / 8); revisedOffset = byteOffset + (bitOffset / 8);
} }
BitFieldDataType bitfieldDt = new BitFieldDBDataType(baseDataType, bitSize, BitFieldDataType bitfieldDt =
storageBitOffset, storageSize, getDataTypeManager()); new BitFieldDBDataType(baseDataType, bitSize, storageBitOffset);
Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(bitfieldDt), key, Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(bitfieldDt), key,
storageSize, ordinal, revisedOffset, componentName, comment); bitfieldDt.getStorageSize(), ordinal, revisedOffset, componentName, comment);
DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
bitfieldDt.addParent(this); // has no affect bitfieldDt.addParent(this); // has no affect
components.add(startIndex, dtc); components.add(startIndex, dtc);
@ -1513,13 +1520,13 @@ class StructureDB extends CompositeDB implements Structure {
try { try {
validateDataType(replacementDt); validateDataType(replacementDt);
if (!(replacementDt instanceof DataTypeDB) || if (!(replacementDt instanceof DataTypeDB) ||
(replacementDt.getDataTypeManager() != getDataTypeManager())) { (replacementDt.getDataTypeManager() != dataMgr)) {
replacementDt = resolve(replacementDt); replacementDt = resolve(replacementDt);
} }
checkAncestry(replacementDt); checkAncestry(replacementDt);
} }
catch (Exception e) { catch (Exception e) {
// TODO: should we use Undefined instead to avoid cases where // TODO: should we use Undefined1 instead to avoid cases where
// DEFAULT datatype can not be used (flex array, bitfield, aligned structure) // DEFAULT datatype can not be used (flex array, bitfield, aligned structure)
// TODO: failing silently is rather hidden // TODO: failing silently is rather hidden
replacementDt = DataType.DEFAULT; replacementDt = DataType.DEFAULT;

View file

@ -121,6 +121,8 @@ class UnionDB extends CompositeDB implements Union {
validateDataType(dataType); validateDataType(dataType);
dataType = adjustBitField(dataType);
dataType = resolve(dataType); dataType = resolve(dataType);
checkAncestry(dataType); checkAncestry(dataType);
@ -165,6 +167,8 @@ class UnionDB extends CompositeDB implements Union {
checkDeleted(); checkDeleted();
validateDataType(dataType); validateDataType(dataType);
dataType = adjustBitField(dataType);
dataType = resolve(dataType); dataType = resolve(dataType);
checkAncestry(dataType); checkAncestry(dataType);
@ -185,53 +189,22 @@ class UnionDB extends CompositeDB implements Union {
} }
@Override @Override
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName,
DataType baseDataType, int bitSize, String componentName, String comment) String comment) throws InvalidDataTypeException {
return insertBitField(components.size(), baseDataType, bitSize, componentName, comment);
}
@Override
public DataTypeComponent insertBitField(int ordinal, DataType baseDataType, int bitSize,
String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { throws InvalidDataTypeException, ArrayIndexOutOfBoundsException {
if (ordinal < 0 || ordinal > components.size()) { if (ordinal < 0 || ordinal > components.size()) {
throw new ArrayIndexOutOfBoundsException(ordinal); throw new ArrayIndexOutOfBoundsException(ordinal);
} }
if (isInternallyAligned()) { BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0);
BitFieldDataType bitFieldDt = return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
new BitFieldDBDataType(baseDataType, bitSize, 0, 0, dataMgr);
return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
}
if (byteWidth <= 0) {
throw new IllegalArgumentException("Invalid byteWidth");
}
// handle unaligned case - use minimal storage
// bitfield value will be forced based upon byteWidth, bitSize and endianess
boolean bigEndian = getDataOrganization().isBigEndian();
int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
int storageSize = BitFieldDataType.getMinimumStorageSize(effectiveBitSize);
if (byteWidth < storageSize) {
throw new IllegalArgumentException(
"Bitfield does not fit within specified constraints");
}
int storageBitOffset = 0;
if (bigEndian) {
storageBitOffset = (8 * storageSize) - effectiveBitSize;
}
BitFieldDataType bitfieldDt = new BitFieldDBDataType(baseDataType, bitSize,
storageBitOffset, storageSize, getDataTypeManager());
DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(bitfieldDt), storageSize,
ordinal, 0, componentName, comment);
bitfieldDt.addParent(this); // currently has no affect
shiftOrdinals(ordinal, 1);
components.add(ordinal, dtc);
adjustLength(true, true);
return dtc;
} }
@Override @Override
@ -434,6 +407,51 @@ class UnionDB extends CompositeDB implements Union {
adjustInternalAlignment(true); adjustInternalAlignment(true);
} }
private DataType adjustBitField(DataType dataType) {
if (!(dataType instanceof BitFieldDataType)) {
return dataType;
}
BitFieldDataType bitfieldDt = (BitFieldDataType) dataType;
DataType baseDataType = bitfieldDt.getBaseDataType();
baseDataType = resolve(baseDataType);
// Both aligned and unaligned bitfields use same adjustment
// unaligned must force bitfield placement at byte offset 0
int bitSize = bitfieldDt.getDeclaredBitSize();
int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
// little-endian always uses bit offset of 0 while
// big-endian offset must be computed
boolean bigEndian = getDataOrganization().isBigEndian();
int storageBitOffset = 0;
if (bigEndian) {
if (bitSize == 0) {
storageBitOffset = 7;
}
else {
int storageSize = BitFieldDataType.getMinimumStorageSize(effectiveBitSize);
storageBitOffset = (8 * storageSize) - effectiveBitSize;
}
}
if (effectiveBitSize != bitfieldDt.getBitSize() ||
storageBitOffset != bitfieldDt.getBitOffset()) {
try {
bitfieldDt =
new BitFieldDBDataType(baseDataType, effectiveBitSize, storageBitOffset);
}
catch (InvalidDataTypeException e) {
// unexpected since deriving from existing bitfield,
// ignore and use existing bitfield
}
}
return bitfieldDt;
}
private void adjustLength(boolean notify, boolean setLastChangeTime) { private void adjustLength(boolean notify, boolean setLastChangeTime) {
lock.acquire(); lock.acquire();
try { try {

View file

@ -202,7 +202,7 @@ public class AlignedStructurePacker {
zeroBitFieldDt.getStorageSize() != 1) { zeroBitFieldDt.getStorageSize() != 1) {
try { try {
BitFieldDataType packedBitFieldDt = new BitFieldDataType( BitFieldDataType packedBitFieldDt = new BitFieldDataType(
zeroBitFieldDt.getBaseDataType(), 0, zeroBitOffset, 1); zeroBitFieldDt.getBaseDataType(), 0, zeroBitOffset);
dataTypeComponent.setDataType(packedBitFieldDt); dataTypeComponent.setDataType(packedBitFieldDt);
} }
catch (InvalidDataTypeException e) { catch (InvalidDataTypeException e) {
@ -451,12 +451,11 @@ public class AlignedStructurePacker {
bitOffset = bitsConsumed; bitOffset = bitsConsumed;
} }
if (bitOffset != currentBitFieldDt.getBitOffset() || if (bitOffset != currentBitFieldDt.getBitOffset()) {
byteSize != currentBitFieldDt.getStorageSize()) {
try { try {
BitFieldDataType packedBitFieldDt = BitFieldDataType packedBitFieldDt =
new BitFieldDataType(currentBitFieldDt.getBaseDataType(), new BitFieldDataType(currentBitFieldDt.getBaseDataType(),
currentBitFieldDt.getDeclaredBitSize(), bitOffset, byteSize); currentBitFieldDt.getDeclaredBitSize(), bitOffset);
dataTypeComponent.setDataType(packedBitFieldDt); dataTypeComponent.setDataType(packedBitFieldDt);
} }
catch (InvalidDataTypeException e) { catch (InvalidDataTypeException e) {

View file

@ -44,24 +44,23 @@ public class BitFieldDataType extends AbstractDataType {
// The bitOffset is established during packing and reflects the right-shift amount within the // The bitOffset is established during packing and reflects the right-shift amount within the
// normalized big-endian view of the allocated byte storage as defined by the corresponding // normalized big-endian view of the allocated byte storage as defined by the corresponding
// composite DataTypeComponent. // composite DataTypeComponent.
private final int bitOffset; // indicates right-shift within big-endian view of component storage private final int bitOffset; // indicates right-shift within big-endian view of component storage (range: 0..7)
private final int storageSize; // component storage size to which bitOffset applies private final int storageSize; // minimal component storage size to which bitOffset applies
protected Settings defaultSettings; protected Settings defaultSettings;
/** /**
* Construct a bit-field type based upon a specified base type. The baseDataType will * Construct a bit-field type based upon a specified base type. The baseDataType will
* take precedence if specified. Either baseType or baseDatatype must be specified. * take precedence if specified. Either baseType or baseDatatype must be specified.
* @param baseDataType base data type (integer/enum type or typedef to same) * @param baseDataType base data type (integer/enum type or typedef to same). This
* @param bitSize size of bit-field expressed as number of bits * bitfield will adopt the same datatype manager as this base type.
* @param bitSize size of bit-field expressed as number of bits (0..255). The effective
* bit size may be reduced based upon the specified base datatype size.
* @param bitOffset right shift factor within storage unit when viewed as a big-endian dd * @param bitOffset right shift factor within storage unit when viewed as a big-endian dd
* scalar value. Based upon minimal storage bitOffset should be in the range 0 to 7. * scalar value. Based upon minimal storage bitOffset should be in the range 0 to 7.
* @param storageSize minimal storage allocation to which bitOffset is applied or 0 to use
* minimum storage size.
* @throws InvalidDataTypeException * @throws InvalidDataTypeException
*/ */
// FIXME: Remove storage parameter (compute based upon bitSize and bitOffset) protected BitFieldDataType(DataType baseDataType, int bitSize, int bitOffset)
protected BitFieldDataType(DataType baseDataType, int bitSize, int bitOffset, int storageSize)
throws InvalidDataTypeException { throws InvalidDataTypeException {
super(CategoryPath.ROOT, baseDataType.getName() + ":" + bitSize, super(CategoryPath.ROOT, baseDataType.getName() + ":" + bitSize,
baseDataType.getDataTypeManager()); baseDataType.getDataTypeManager());
@ -76,14 +75,7 @@ public class BitFieldDataType extends AbstractDataType {
this.bitSize = bitSize; this.bitSize = bitSize;
this.bitOffset = bitOffset; this.bitOffset = bitOffset;
effectiveBitSize = getEffectiveBitSize(bitSize, this.baseDataType.getLength()); effectiveBitSize = getEffectiveBitSize(bitSize, this.baseDataType.getLength());
if (storageSize == 0) { storageSize = getMinimumStorageSize(effectiveBitSize, bitOffset);
storageSize = getMinimumStorageSize(effectiveBitSize, bitOffset);
}
this.storageSize = storageSize;
checkStorage();
if (bitOffset < 0 || bitOffset > ((8 * storageSize) - effectiveBitSize)) {
throw new InvalidDataTypeException("invalid bit offset: " + bitOffset);
}
this.defaultSettings = this.baseDataType.getDefaultSettings(); this.defaultSettings = this.baseDataType.getDefaultSettings();
} }
@ -95,14 +87,7 @@ public class BitFieldDataType extends AbstractDataType {
* @throws InvalidDataTypeException if specified baseDataType is not permitted * @throws InvalidDataTypeException if specified baseDataType is not permitted
*/ */
protected BitFieldDataType(DataType baseDataType, int bitSize) throws InvalidDataTypeException { protected BitFieldDataType(DataType baseDataType, int bitSize) throws InvalidDataTypeException {
this(baseDataType, bitSize, 0, 0); this(baseDataType, bitSize, 0);
}
private void checkStorage() throws IllegalArgumentException {
int minimumStorageSize = getMinimumStorageSize(effectiveBitSize);
if (storageSize != minimumStorageSize && storageSize != ++minimumStorageSize) {
throw new IllegalArgumentException("minimal storage size required");
}
} }
/** /**
@ -197,6 +182,7 @@ public class BitFieldDataType extends AbstractDataType {
/** /**
* Get the packing storage size in bytes associated with this bit-field which may be * Get the packing storage size in bytes associated with this bit-field which may be
* larger than the base type associated with the fields original definition. * larger than the base type associated with the fields original definition.
* Returned value is the same as {@link #getLength()}.
* @return packing storage size in bytes * @return packing storage size in bytes
*/ */
public int getStorageSize() { public int getStorageSize() {
@ -320,13 +306,20 @@ public class BitFieldDataType extends AbstractDataType {
return clone(dtm); return clone(dtm);
} }
/**
* Clone this bitfield to a new datatype manager. This may change the effective bit
* size and storage size of the resulting datatype based upon the data organization
* of the specified dtm.
* @param dtm target datatype manager
* @return new instance or same instance of dtm is unchanged.
*/
@Override @Override
public BitFieldDataType clone(DataTypeManager dtm) { public BitFieldDataType clone(DataTypeManager dtm) {
if (dtm == dataMgr) { if (dtm == dataMgr) {
return this; return this;
} }
try { try {
return new BitFieldDataType(baseDataType.clone(dtm), bitSize, bitOffset, storageSize); return new BitFieldDataType(baseDataType.clone(dtm), bitSize, bitOffset);
} }
catch (InvalidDataTypeException e) { catch (InvalidDataTypeException e) {
throw new AssertException("unexpected", e); throw new AssertException("unexpected", e);

View file

@ -201,44 +201,6 @@ public interface Composite extends DataType {
*/ */
public DataTypeComponent insert(int ordinal, DataType dataType, int length); public DataTypeComponent insert(int ordinal, DataType dataType, int length);
/**
* Inserts a new bitfield at the specified ordinal position in this composite.
* Within aligned composites and unions the specified byteWidth and bitOffset will be
* ignored.
* The component length will be computed based upon the specified parameters and will
* be reduced from byteWidth to its minimal size for the new component.
* <p>
* For unaligned structures, a component shift will only occur if the bitfield placement
* conflicts with another component. If no conflict occurs, the bitfield will be placed
* at the specified location consuming any DEFAULT components as needed. When a conflict
* does occur a shift will be performed at the ordinal position based upon the specified
* byteWidth. When located onto existing bitfields they will be packed together
* provided they do not conflict, otherwise the conflict rule above applies.
* <p>
* Supported packing for little-endian fills lsb first, whereas big-endian fills msb first.
* Insertion behavior may not work as expected if packing rules differ from this.
* @param ordinal the ordinal where the new datatype is to be inserted.
* @param byteWidth the storage unit width which contains the bitfield. Must be large
* enough to contain the specified bitSize and corresponding bitOffset. The actual
* component size used will be recomputed during insertion.
* @param bitOffset corresponds to the bitfield left-shift amount with the storage
* unit when viewed as big-endian. The final offset may be reduced based upon
* the minimal storage size determined during insertion.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the bitfield size in bits
* @param componentName the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the componentDataType created whose associated data type will
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified data type is
* not a valid base type for bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the
* current number of components.
*/
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
/** /**
* Inserts a new datatype at the specified ordinal position in this composite. * Inserts a new datatype at the specified ordinal position in this composite.
* <BR>Note: For an aligned structure the ordinal position will get adjusted * <BR>Note: For an aligned structure the ordinal position will get adjusted

View file

@ -17,34 +17,6 @@ package ghidra.program.model.data;
public class CompositeAlignmentHelper { public class CompositeAlignmentHelper {
private static int getImpartedAlignment(DataOrganization dataOrganization, int packingAlignment,
DataTypeComponent dataTypeComponent) {
// FIXME: try to eliminate this method.
// DataType componentDt = dataTypeComponent.getDataType();
//
// if (componentDt instanceof BitFieldDataType) {
// BitFieldPacking bitFieldPacking = dataOrganization.getBitFieldPacking();
// if (!bitFieldPacking.isTypeAlignmentEnabled() ||
// isBitFieldPackingEnabled(dataOrganization, packingAlignment)) {
// return 0;
// }
// BitFieldDataType bitFieldDt = (BitFieldDataType) componentDt;
// // zero-length bitfield assumed not to influence composite alignment, only component alignment
// if (!bitFieldPacking.zeroLengthAffectsContainerAlignment() &&
// bitFieldDt.getBitSize() == 0) {
// return 0;
// }
// // largest bit-field base-type will provide composite alignment constraint
// return getPackedAlignment(dataOrganization, packingAlignment,
// bitFieldDt.getBaseDataType(), bitFieldDt.getBaseTypeSize());
// }
return CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packingAlignment,
dataTypeComponent);
}
private static int getCompositeAlignmentMultiple(DataOrganization dataOrganization, private static int getCompositeAlignmentMultiple(DataOrganization dataOrganization,
Composite composite) { Composite composite) {
int allComponentsLCM = 1; int allComponentsLCM = 1;
@ -140,4 +112,5 @@ public class CompositeAlignmentHelper {
return ((absoluteMaxAlignment == 0) || (lcm < absoluteMaxAlignment)) ? lcm return ((absoluteMaxAlignment == 0) || (lcm < absoluteMaxAlignment)) ? lcm
: absoluteMaxAlignment; : absoluteMaxAlignment;
} }
} }

View file

@ -141,17 +141,6 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
} }
} }
@Override
public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName,
String comment) throws InvalidDataTypeException {
BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(getDataTypeManager());
BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize);
return add(bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
}
/** /**
* Handle replacement of datatype which may impact bitfield datatype. * Handle replacement of datatype which may impact bitfield datatype.
* @param bitfieldComponent bitfield component * @param bitfieldComponent bitfield component
@ -180,9 +169,8 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
} }
try { try {
BitFieldDataType newBitfieldDt = BitFieldDataType newBitfieldDt = new BitFieldDataType(newDt,
new BitFieldDataType(newDt, bitfieldDt.getDeclaredBitSize(), bitfieldDt.getDeclaredBitSize(), bitfieldDt.getBitOffset());
bitfieldDt.getBitOffset(), bitfieldDt.getStorageSize());
bitfieldComponent.setDataType(newBitfieldDt); bitfieldComponent.setDataType(newBitfieldDt);
oldDt.removeParent(this); oldDt.removeParent(this);
newDt.addParent(this); newDt.addParent(this);

View file

@ -66,6 +66,46 @@ public interface Structure extends Composite {
*/ */
public abstract DataTypeComponent getDataTypeAt(int offset); public abstract DataTypeComponent getDataTypeAt(int offset);
/**
* Inserts a new bitfield at the specified ordinal position in this structure.
* Within aligned structures the specified byteWidth and bitOffset will be
* ignored since packing will occur at the specified ordinal position.
* The resulting component length and bitfield details will reflect the use
* of minimal storage sizing.
* <p>
* For unaligned structures, a component shift will only occur if the bitfield placement
* conflicts with another component. If no conflict occurs, the bitfield will be placed
* at the specified location consuming any DEFAULT components as needed. When a conflict
* does occur a shift will be performed at the ordinal position based upon the specified
* byteWidth. When located onto existing bitfields they will be packed together
* provided they do not conflict, otherwise the conflict rule above applies.
* <p>
* Supported aligned packing starts with bit-0 (lsb) of the first byte for little-endian, and
* with bit-7 (msb) of the first byte for big-endian. This is the default behavior for most
* compilers. Insertion behavior may not work as expected if packing rules differ from this.
* @param ordinal the ordinal where the new datatype is to be inserted.
* @param byteWidth the storage allocation unit width which contains the bitfield. Must be large
* enough to contain the "effective bit size" and corresponding bitOffset. The actual
* component size used will be recomputed during insertion.
* @param bitOffset corresponds to the bitfield left-shift amount with the storage
* unit when viewed as big-endian. The final offset may be reduced based upon
* the minimal storage size determined during insertion.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the declared bitfield size in bits. The effective bit size may be
* adjusted based upon the specified baseDataType.
* @param componentName the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the bitfield component created whose associated data type will
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is
* not a valid base type for bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the
* current number of components.
*/
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
/** /**
* Inserts a new bitfield at the specified location in this composite. * Inserts a new bitfield at the specified location in this composite.
* This method is intended to be used with unaligned structures where * This method is intended to be used with unaligned structures where

View file

@ -297,7 +297,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
} }
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(getDataTypeManager()); dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
if ((offset > structLength) && !isInternallyAligned()) { if ((offset > structLength) && !isInternallyAligned()) {
@ -369,7 +369,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(getDataTypeManager()); dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
DataTypeComponentImpl dtc; DataTypeComponentImpl dtc;
@ -437,7 +437,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
} }
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(getDataTypeManager()); dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
int idx; int idx;
@ -481,6 +481,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return dtc; return dtc;
} }
@Override
public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName,
String comment) throws InvalidDataTypeException {
BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(dataMgr);
BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize);
return add(bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
}
@Override @Override
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment) DataType baseDataType, int bitSize, String componentName, String comment)
@ -491,7 +502,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
} }
BitFieldDataType.checkBaseDataType(baseDataType); BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(getDataTypeManager()); baseDataType = baseDataType.clone(dataMgr);
if (!isInternallyAligned()) { if (!isInternallyAligned()) {
int offset = structLength; int offset = structLength;
@ -521,7 +532,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
} }
BitFieldDataType.checkBaseDataType(baseDataType); BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(getDataTypeManager()); baseDataType = baseDataType.clone(dataMgr);
int effectiveBitSize = int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength()); BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
@ -602,11 +613,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
structLength = requiredLength; structLength = requiredLength;
} }
// use minimal storage // adjust for minimal storage use
int storageBitOffset = bitOffset % 8; int storageBitOffset = bitOffset % 8;
int storageSize =
BitFieldDataType.getMinimumStorageSize(effectiveBitSize + storageBitOffset);
int revisedOffset; int revisedOffset;
if (bigEndian) { if (bigEndian) {
revisedOffset = byteOffset + byteWidth - ((effectiveBitSize + bitOffset + 7) / 8); revisedOffset = byteOffset + byteWidth - ((effectiveBitSize + bitOffset + 7) / 8);
@ -615,11 +623,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
revisedOffset = byteOffset + (bitOffset / 8); revisedOffset = byteOffset + (bitOffset / 8);
} }
BitFieldDataType bitfieldDt = BitFieldDataType bitfieldDt = new BitFieldDataType(baseDataType, bitSize, storageBitOffset);
new BitFieldDataType(baseDataType, bitSize, storageBitOffset, storageSize);
DataTypeComponentImpl dtc = new DataTypeComponentImpl(bitfieldDt, this, storageSize, DataTypeComponentImpl dtc = new DataTypeComponentImpl(bitfieldDt, this,
ordinal, revisedOffset, componentName, comment); bitfieldDt.getStorageSize(), ordinal, revisedOffset, componentName, comment);
bitfieldDt.addParent(this); // currently has no affect bitfieldDt.addParent(this); // currently has no affect
components.add(startIndex, dtc); components.add(startIndex, dtc);
@ -845,7 +852,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
@Override @Override
public DataType clone(DataTypeManager dtm) { public DataType clone(DataTypeManager dtm) {
if (getDataTypeManager() == dtm) { if (dataMgr == dtm) {
return this; return this;
} }
StructureDataType struct = StructureDataType struct =
@ -991,13 +998,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
DataType newDt = replacementDt; DataType newDt = replacementDt;
try { try {
validateDataType(replacementDt); validateDataType(replacementDt);
if (replacementDt.getDataTypeManager() != getDataTypeManager()) { if (replacementDt.getDataTypeManager() != dataMgr) {
replacementDt = replacementDt.clone(dataMgr); replacementDt = replacementDt.clone(dataMgr);
} }
checkAncestry(replacementDt); checkAncestry(replacementDt);
} }
catch (Exception e) { catch (Exception e) {
// TODO: should we use Undefined instead to avoid cases where // TODO: should we use Undefined1 instead to avoid cases where
// DEFAULT datatype can not be used (flex array, bitfield, aligned structure) // DEFAULT datatype can not be used (flex array, bitfield, aligned structure)
// TODO: failing silently is rather hidden // TODO: failing silently is rather hidden
replacementDt = DataType.DEFAULT; replacementDt = DataType.DEFAULT;
@ -1130,7 +1137,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return getComponent(index); return getComponent(index);
} }
dataType = dataType.clone(getDataTypeManager()); dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
length = getPreferredComponentLength(dataType, length); length = getPreferredComponentLength(dataType, length);
@ -1173,7 +1180,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return getComponent(ordinal); return getComponent(ordinal);
} }
dataType = dataType.clone(getDataTypeManager()); dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
length = getPreferredComponentLength(dataType, length); length = getPreferredComponentLength(dataType, length);

View file

@ -20,4 +20,26 @@ package ghidra.program.model.data;
*/ */
public interface Union extends Composite { public interface Union extends Composite {
/**
* Inserts a new bitfield at the specified ordinal position in this union.
* For both aligned and unaligned unions the bitfield starts with bit-0 (lsb) of the first byte
* for little-endian, and with bit-7 (msb) of the first byte for big-endian. This is the
* default behavior for most compilers. Insertion behavior may not work as expected if
* packing rules differ from this.
* @param ordinal the ordinal where the new datatype is to be inserted.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the declared bitfield size in bits. The effective bit size may be
* adjusted based upon the specified baseDataType.
* @param componentName the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the bitfield component created whose associated data type will
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is
* not a valid base type for bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the
* current number of components.
*/
public DataTypeComponent insertBitField(int ordinal, DataType baseDataType, int bitSize,
String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
} }

View file

@ -129,7 +129,9 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(getDataTypeManager()); dataType = adjustBitField(dataType);
dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
length = getPreferredComponentLength(dataType, length); length = getPreferredComponentLength(dataType, length);
@ -148,7 +150,9 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(getDataTypeManager()); dataType = adjustBitField(dataType);
dataType = dataType.clone(dataMgr);
checkAncestry(dataType); checkAncestry(dataType);
length = getPreferredComponentLength(dataType, length); length = getPreferredComponentLength(dataType, length);
@ -164,8 +168,14 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
} }
@Override @Override
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, public DataTypeComponent addBitField(DataType baseDataType, int bitSize, String componentName,
DataType baseDataType, int bitSize, String componentName, String comment) String comment) throws InvalidDataTypeException {
return insertBitField(components.size(), baseDataType, bitSize, componentName, comment);
}
@Override
public DataTypeComponent insertBitField(int ordinal, DataType baseDataType, int bitSize,
String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { throws InvalidDataTypeException, ArrayIndexOutOfBoundsException {
if (ordinal < 0 || ordinal > components.size()) { if (ordinal < 0 || ordinal > components.size()) {
@ -173,48 +183,10 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
} }
BitFieldDataType.checkBaseDataType(baseDataType); BitFieldDataType.checkBaseDataType(baseDataType);
baseDataType = baseDataType.clone(getDataTypeManager()); baseDataType = baseDataType.clone(dataMgr);
if (isInternallyAligned()) { BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize);
BitFieldDataType bitFieldDt = new BitFieldDataType(baseDataType, bitSize); return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
}
if (byteWidth <= 0) {
throw new IllegalArgumentException("Invalid byteWidth");
}
// TODO: How should zero-length bitfields be handled ?
// handle unaligned case - use minimal storage
// bitfield value will be forced based upon byteWidth, bitSize and endianess
boolean bigEndian = getDataOrganization().isBigEndian();
int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
int storageSize = BitFieldDataType.getMinimumStorageSize(effectiveBitSize);
if (byteWidth < storageSize) {
throw new IllegalArgumentException(
"Bitfield does not fit within specified constraints");
}
int storageBitOffset = 0;
if (bigEndian) {
storageBitOffset = (8 * storageSize) - effectiveBitSize;
}
BitFieldDataType bitfieldDt =
new BitFieldDataType(baseDataType, bitSize, storageBitOffset, storageSize);
DataTypeComponentImpl dtc = new DataTypeComponentImpl(bitfieldDt, this, storageSize,
ordinal, 0, componentName, comment);
bitfieldDt.addParent(this); // currently has no affect
shiftOrdinals(ordinal, 1);
components.add(ordinal, dtc);
adjustLength(true);
return dtc;
} }
@Override @Override
@ -227,7 +199,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
@Override @Override
public DataType clone(DataTypeManager dtm) { public DataType clone(DataTypeManager dtm) {
if (getDataTypeManager() == dtm) { if (dataMgr == dtm) {
return this; return this;
} }
UnionDataType union = new UnionDataType(getCategoryPath(), getName(), getUniversalID(), UnionDataType union = new UnionDataType(getCategoryPath(), getName(), getUniversalID(),
@ -260,6 +232,50 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
} }
} }
private DataType adjustBitField(DataType dataType) {
if (!(dataType instanceof BitFieldDataType)) {
return dataType;
}
BitFieldDataType bitfieldDt = (BitFieldDataType) dataType;
DataType baseDataType = bitfieldDt.getBaseDataType();
baseDataType = baseDataType.clone(dataMgr);
// Both aligned and unaligned bitfields use same adjustment
// unaligned must force bitfield placement at byte offset 0
int bitSize = bitfieldDt.getDeclaredBitSize();
int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
// little-endian always uses bit offset of 0 while
// big-endian offset must be computed
boolean bigEndian = getDataOrganization().isBigEndian();
int storageBitOffset = 0;
if (bigEndian) {
if (bitSize == 0) {
storageBitOffset = 7;
}
else {
int storageSize = BitFieldDataType.getMinimumStorageSize(effectiveBitSize);
storageBitOffset = (8 * storageSize) - effectiveBitSize;
}
}
if (effectiveBitSize != bitfieldDt.getBitSize() ||
storageBitOffset != bitfieldDt.getBitOffset()) {
try {
bitfieldDt = new BitFieldDataType(baseDataType, effectiveBitSize, storageBitOffset);
}
catch (InvalidDataTypeException e) {
// unexpected since deriving from existing bitfield,
// ignore and use existing bitfield
}
}
return bitfieldDt;
}
private void adjustLength(boolean notify) { private void adjustLength(boolean notify) {
int oldLength = unionLength; int oldLength = unionLength;
unionLength = 0; unionLength = 0;
@ -496,4 +512,5 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
public void adjustInternalAlignment() { public void adjustInternalAlignment() {
adjustLength(true); adjustLength(true);
} }
} }

View file

@ -25,7 +25,7 @@ import ghidra.program.model.data.*;
public class BitFieldDBDataTypeTest extends AbstractGTest { public class BitFieldDBDataTypeTest extends AbstractGTest {
private DataTypeManager dataMgr; private DataTypeManagerDB dataMgr;
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
@ -36,29 +36,29 @@ public class BitFieldDBDataTypeTest extends AbstractGTest {
@Test @Test
public void testGetIdAndGetDataTypeFromId() throws Exception { public void testGetIdAndGetDataTypeFromId() throws Exception {
testRoundTrip(new BitFieldDBDataType(CharDataType.dataType, 1, 4, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(CharDataType.dataType, 1, 4));
testRoundTrip(new BitFieldDBDataType(CharDataType.dataType, 2, 6, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(CharDataType.dataType, 2, 6));
testRoundTrip(new BitFieldDBDataType(ShortDataType.dataType, 3, 2, 2, dataMgr)); testRoundTrip(new BitFieldDBDataType(ShortDataType.dataType, 3, 2));
testRoundTrip(new BitFieldDBDataType(UnsignedShortDataType.dataType, 4, 4, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(UnsignedShortDataType.dataType, 4, 4));
testRoundTrip(new BitFieldDBDataType(IntegerDataType.dataType, 5, 7, 2, dataMgr)); testRoundTrip(new BitFieldDBDataType(IntegerDataType.dataType, 5, 7));
testRoundTrip(new BitFieldDBDataType(UnsignedIntegerDataType.dataType, 14, 2, 3, dataMgr)); testRoundTrip(new BitFieldDBDataType(UnsignedIntegerDataType.dataType, 14, 2));
testRoundTrip(new BitFieldDBDataType(LongDataType.dataType, 27, 2, 5, dataMgr)); testRoundTrip(new BitFieldDBDataType(LongDataType.dataType, 27, 2));
testRoundTrip(new BitFieldDBDataType(UnsignedLongDataType.dataType, 6, 0, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(UnsignedLongDataType.dataType, 6, 0));
testRoundTrip(new BitFieldDBDataType(LongLongDataType.dataType, 6, 2, 2, dataMgr)); testRoundTrip(new BitFieldDBDataType(LongLongDataType.dataType, 6, 2));
testRoundTrip(new BitFieldDBDataType(UnsignedLongLongDataType.dataType, 6, 2, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(UnsignedLongLongDataType.dataType, 6, 2));
// non-standard integer base types // non-standard integer base types
testRoundTrip(new BitFieldDBDataType(ByteDataType.dataType, 6, 2, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(ByteDataType.dataType, 6, 2));
testRoundTrip(new BitFieldDBDataType(QWordDataType.dataType, 6, 2, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(QWordDataType.dataType, 6, 2));
// TypeDef base types // TypeDef base types
TypeDef foo = new TypedefDataType("foo", IntegerDataType.dataType); TypeDef foo = new TypedefDataType("foo", IntegerDataType.dataType);
testRoundTrip(new BitFieldDBDataType(foo, 6, 3, 2, dataMgr)); testRoundTrip(new BitFieldDBDataType(foo, 6, 3));
// Enum base types // Enum base types
EnumDataType fum = new EnumDataType("fum", 4); EnumDataType fum = new EnumDataType("fum", 4);
fum.add("A", 1); fum.add("A", 1);
testRoundTrip(new BitFieldDBDataType(fum, 6, 2, 1, dataMgr)); testRoundTrip(new BitFieldDBDataType(fum, 6, 2));
} }

View file

@ -164,7 +164,7 @@ public class UnionDBTest extends AbstractGTest {
union.delete(0); union.delete(0);
} }
// NOTE: bitOffset ignored for union // NOTE: bitOffset ignored for union
union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment"); union.insertBitField(0, IntegerDataType.dataType, 2, "bf1", "bf1Comment");
union.insert(0, ShortDataType.dataType); union.insert(0, ShortDataType.dataType);
//@formatter:off //@formatter:off
@ -185,7 +185,7 @@ public class UnionDBTest extends AbstractGTest {
for (int i = 0; i < cnt; i++) { for (int i = 0; i < cnt; i++) {
union.delete(0); union.delete(0);
} }
union.insertBitField(0, 4, 12, IntegerDataType.dataType, 2, "bf1", "bf1Comment"); union.insertBitField(0, IntegerDataType.dataType, 2, "bf1", "bf1Comment");
union.insert(0, ShortDataType.dataType); union.insert(0, ShortDataType.dataType);
union.setInternallyAligned(true); union.setInternallyAligned(true);
@ -203,8 +203,8 @@ public class UnionDBTest extends AbstractGTest {
@Test @Test
public void testInsertBitFieldLittleEndian() throws Exception { public void testInsertBitFieldLittleEndian() throws Exception {
union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); union.insertBitField(2, IntegerDataType.dataType, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); union.insertBitField(3, ByteDataType.dataType, 4, "bf2", "bf2Comment");
//@formatter:off //@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
@ -226,8 +226,8 @@ public class UnionDBTest extends AbstractGTest {
transitionToBigEndian(); transitionToBigEndian();
union.insertBitField(2, 4, 0, IntegerDataType.dataType, 4, "bf1", "bf1Comment"); union.insertBitField(2, IntegerDataType.dataType, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, ByteDataType.dataType, 4, "bf2", "bf2Comment"); union.insertBitField(3, ByteDataType.dataType, 4, "bf2", "bf2Comment");
//@formatter:off //@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
@ -250,8 +250,8 @@ public class UnionDBTest extends AbstractGTest {
TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType);
td = (TypeDef) dataMgr.resolve(td, null); td = (TypeDef) dataMgr.resolve(td, null);
union.insertBitField(2, 4, 0, td, 4, "bf1", "bf1Comment"); union.insertBitField(2, td, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, td, 4, "bf2", "bf2Comment"); union.insertBitField(3, td, 4, "bf2", "bf2Comment");
//@formatter:off //@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
@ -289,8 +289,8 @@ public class UnionDBTest extends AbstractGTest {
TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType);
td = (TypeDef) dataMgr.resolve(td, null); td = (TypeDef) dataMgr.resolve(td, null);
union.insertBitField(2, 4, 0, td, 4, "bf1", "bf1Comment"); union.insertBitField(2, td, 4, "bf1", "bf1Comment");
union.insertBitField(3, 1, 0, td, 4, "bf2", "bf2Comment"); union.insertBitField(3, td, 4, "bf2", "bf2Comment");
//@formatter:off //@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" + CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +

View file

@ -177,13 +177,11 @@ public class BitFieldDataTypeTest extends AbstractGTest {
} }
private BitFieldDataType bitField(int size, int offset) throws Exception { private BitFieldDataType bitField(int size, int offset) throws Exception {
int storageSize = BitFieldDataType.getMinimumStorageSize(size); return new BitFieldDataType(IntegerDataType.dataType, size, offset);
return new BitFieldDataType(IntegerDataType.dataType, size, offset, storageSize);
} }
private BitFieldDataType unsignedBitField(int size, int offset) throws Exception { private BitFieldDataType unsignedBitField(int size, int offset) throws Exception {
int storageSize = BitFieldDataType.getMinimumStorageSize(size); return new BitFieldDataType(UnsignedIntegerDataType.dataType, size, offset);
return new BitFieldDataType(UnsignedIntegerDataType.dataType, size, offset, storageSize);
} }
private MemBuffer membuf(int... bytes) throws Exception { private MemBuffer membuf(int... bytes) throws Exception {