diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java index e7483cc766..bc58280672 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java @@ -302,20 +302,16 @@ public class PreCommentFieldFactory extends FieldFactory { if (dt instanceof Structure) { Structure struct = (Structure) dt; - lastDtc = struct.getComponentContaining(struct.getLength()); - int lastDtcOrdinal = struct.getNumComponents() - 1; - while (lastDtc != null && lastDtc.isBitFieldComponent() && - lastDtc.getOrdinal() < lastDtcOrdinal) { - lastDtc = struct.getComponent(lastDtc.getOrdinal() + 1); - } + List components = + struct.getComponentsContaining(struct.getLength()); + lastDtc = components.isEmpty() ? null : components.get(components.size() - 1); } else if (dt instanceof DynamicDataType) { DynamicDataType ddt = (DynamicDataType) dt; lastDtc = ddt.getComponentAt(data.getLength(), data); - int lastDtcOrdinal = ddt.getNumComponents(data); - while (lastDtc != null && lastDtc.isBitFieldComponent() && - lastDtc.getOrdinal() < lastDtcOrdinal) { - lastDtc = ddt.getComponent(lastDtc.getOrdinal() + 1, data); + int lastDtcOrdinal = ddt.getNumComponents(data) - 1; + if (lastDtc != null && lastDtc.getOrdinal() < lastDtcOrdinal) { + lastDtc = ddt.getComponent(lastDtcOrdinal, data); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java index 8a5c097ef9..e82e702db5 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporterTest.java @@ -778,20 +778,20 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { DebugInfoEntry structDIE = newStruct("mystruct", 100).create(cu); newMember(structDIE, "f1", intDIE, 0).create(cu); - newMember(structDIE, "flexarray", arrayDIE, 99).create(cu); + newMember(structDIE, "flexarray", arrayDIE, 100).create(cu); importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - DataTypeComponent component = structdt.getComponentContaining(99); - assertNotNull(component); + List components = structdt.getComponentsContaining(100); + assertEquals(1, components.size()); + DataTypeComponent component = components.get(0); assertEquals(0, component.getLength()); DataType dt = component.getDataType(); assertTrue(dt.isEquivalent(new ArrayDataType(IntegerDataType.dataType, 0, -1))); assertEquals(100, structdt.getLength()); - } @Test diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java index 7333fb16b8..58704140f9 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java @@ -1477,11 +1477,158 @@ public class StructureDataTypeTest extends AbstractGTest { } @Test - public void testgetComponentContaining() { + public void testGetComponentAt() { + /** + * /TestStruct + * pack(disabled) + * Structure TestStruct { + * 0 byte 1 field1 "Comment1" + * 1 word 2 null "Comment2" + * 3 dword 4 field3 "" + * 7 byte 1 field4 "Comment4" + * } + * Size = 8 Actual Alignment = 1 + */ + + DataTypeComponent dtc = struct.getComponentAt(3); + assertEquals(" 2 3 dword 4 field3 null", dtc.toString()); + + dtc = struct.getComponentAt(4); // offcut + assertNull(dtc); + + assertEquals(8, struct.getLength()); + + dtc = struct.getComponentAt(8); + assertNull(dtc); + + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null); + struct.add(new LongDataType(), "field4", null); + struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null); + + assertEquals(12, struct.getLength()); + + /** + * /TestStruct + * pack(disabled) + * Structure TestStruct { + * 0 byte 1 field1 "Comment1" + * 1 word 2 null "Comment2" + * 3 dword 4 field3 "" + * 7 byte 1 field4 "Comment4" + * 8 char[0] 0 zarray1 "" + * 8 long 4 field4 "" + * 12 long[0] 0 zarray2 "" + * } + * Size = 12 Actual Alignment = 1 + */ + + dtc = struct.getComponentAt(8); + assertEquals(" 5 8 long 4 field4 null", dtc.toString()); + + dtc = struct.getComponentAt(9); // offcut + assertNull(dtc); + + dtc = struct.getComponentAt(12); // end-of-struct + assertNull(dtc); + + // force components to align + struct.setPackingEnabled(true); + + /** + * /Test + * pack(disabled) + * Structure Test { + * 0 byte 1 field1 "Comment1" + * 2 word 2 null "Comment2" + * 4 dword 4 field3 "" + * 8 byte 1 field4 "Comment4" + * 9 char[0] 0 zarray1 "" + * 12 long 4 field4 "" + * 16 long[0] 0 zarray2 "" + * } + * Size = 16 Actual Alignment = 1 + */ + + assertEquals(16, struct.getLength()); + + dtc = struct.getComponentAt(9); // offset of zero-length component + assertNull(dtc); + + struct.setPackingEnabled(false); + + dtc = struct.getComponentAt(9); // undefined at offset of zero-length component + assertEquals(" 5 9 undefined 1 null null", dtc.toString()); + + } + + @Test + public void testGetComponentContaining() { DataTypeComponent dtc = struct.getComponentContaining(4); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - assertEquals(2, dtc.getOrdinal()); - assertEquals(3, dtc.getOffset()); + assertEquals(" 2 3 dword 4 field3 null", dtc.toString()); + + assertEquals(8, struct.getLength()); + + dtc = struct.getComponentContaining(8); + assertNull(dtc); + + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null); + struct.add(new LongDataType(), "field4", null); + struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null); + + assertEquals(12, struct.getLength()); + + /** + * /TestStruct + * pack(disabled) + * Structure TestStruct { + * 0 byte 1 field1 "Comment1" + * 1 word 2 null "Comment2" + * 3 dword 4 field3 "" + * 7 byte 1 field4 "Comment4" + * 8 char[0] 0 zarray1 "" + * 8 long 4 field4 "" + * 12 long[0] 0 zarray2 "" + * } + * Size = 12 Actual Alignment = 1 + */ + + dtc = struct.getComponentContaining(8); + assertEquals(" 5 8 long 4 field4 null", dtc.toString()); + + dtc = struct.getComponentContaining(9); // offcut + assertEquals(" 5 8 long 4 field4 null", dtc.toString()); + + dtc = struct.getComponentContaining(12); // end-of-struct + assertNull(dtc); + + // force components to align + struct.setPackingEnabled(true); + + /** + * /Test + * pack(disabled) + * Structure Test { + * 0 byte 1 field1 "Comment1" + * 2 word 2 null "Comment2" + * 4 dword 4 field3 "" + * 8 byte 1 field4 "Comment4" + * 9 char[0] 0 zarray1 "" + * 12 long 4 field4 "" + * 16 long[0] 0 zarray2 "" + * } + * Size = 16 Actual Alignment = 1 + */ + + assertEquals(16, struct.getLength()); + + dtc = struct.getComponentContaining(9); // offset of zero-length component + assertNull(dtc); + + struct.setPackingEnabled(false); + + dtc = struct.getComponentContaining(9); // undefined at offset of zero-length component + assertEquals(" 5 9 undefined 1 null null", dtc.toString()); + } @Test diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java index 1ec28f5f9c..79b8cb7a57 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java @@ -345,8 +345,7 @@ public class EditStructureUtils { while (index >= 0) { monitor.checkCanceled(); DataTypeComponent component = structure.getComponentAt(index); - if (component.getDataType().getName().equals("undefined") && - component.getLength() == 1) { + if (component != null && component.getDataType() == DataType.DEFAULT) { index--; numUndefineds++; } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeFieldAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeFieldAction.java index 5728dd17c2..0ca880605b 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeFieldAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RetypeFieldAction.java @@ -17,7 +17,6 @@ package ghidra.app.plugin.core.decompile.actions; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; -import java.util.List; import docking.action.KeyBindingData; import docking.action.MenuData; @@ -67,21 +66,6 @@ public class RetypeFieldAction extends AbstractDecompilerAction { return false; } - private DataTypeComponent getComponentContaining(Structure struct, int offset) { - DataTypeComponent comp = null; - List components = struct.getComponentsContaining(offset); - if (components != null) { - for (DataTypeComponent c : components) { - // skip components not supported by decompiler - if (c.getLength() != 0) { - comp = c; - break; - } - } - } - return comp; - } - @Override protected void decompilerActionPerformed(DecompilerActionContext context) { Program program = context.getProgram(); @@ -102,8 +86,14 @@ public class RetypeFieldAction extends AbstractDecompilerAction { return; } - // Get original component and datatype - DataTypeComponent comp = getComponentContaining(struct, offset); + // Get original component and datatype - structure may be packed so an offset which corresponds + // to padding byte may return null + DataTypeComponent comp = struct.getComponentContaining(offset); + if (comp != null && comp.getOffset() != offset) { + Msg.showError(this, tool.getToolFrame(), "Retype Failed", + "Retype offset does not correspond to start of component"); + return; + } DataType originalDataType = comp != null ? comp.getDataType() : DataType.DEFAULT; if (originalDataType instanceof BitFieldDataType) { Msg.showError(this, tool.getToolFrame(), "Retype Failed", diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index 30a698c42d..3caebcf105 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -913,8 +913,8 @@ class StructureDB extends CompositeDB implements StructureInternal { } /** - * Backup from specified ordinal to the first component which contains the specified offset. - * @param index defined component index + * Backup from specified defined-component index to the first component which contains the specified offset. + * @param index any defined component index which contains offset * @param offset offset within structure * @return index of first defined component containing specific offset. */ @@ -933,8 +933,28 @@ class StructureDB extends CompositeDB implements StructureInternal { } /** - * Advance from specified ordinal to the last component which contains the specified offset. - * @param index defined component index + * Identify defined-component index of the first non-zero-length component which contains the specified offset. + * If only zero-length components exist, the last zero-length component which contains the offset will be returned. + * @param index any defined component index which contains offset + * @param offset offset within structure + * @return index of first defined component containing specific offset. + */ + private int indexOfFirstNonZeroLenComponentContainingOffset(int index, int offset) { + index = backupToFirstComponentContainingOffset(index, offset); + DataTypeComponentDB next = components.get(index); + while (next.getLength() == 0 && index < (components.size() - 1)) { + next = components.get(index + 1); + if (!next.containsOffset(offset)) { + break; + } + ++index; + } + return index; + } + + /** + * Advance from specified defined-component index to the last component which contains the specified offset. + * @param index any defined component index which contains offset * @param offset offset within structure * @return index of last defined component containing specific offset. */ @@ -1070,9 +1090,12 @@ class StructureDB extends CompositeDB implements StructureInternal { if (index >= 0) { // return first matching defined component containing offset DataTypeComponent dtc = components.get(index); - index = backupToFirstComponentContainingOffset(index, offset); + index = indexOfFirstNonZeroLenComponentContainingOffset(index, offset); dtc = components.get(index); - return dtc; + if (dtc.getLength() != 0) { + return dtc; + } + index = -index - 1; } if (offset != structLength && !isPackingEnabled()) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java index b56df48fc1..504e94a646 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java @@ -116,10 +116,11 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { * to share the same offset. * @param offset the offset into the dataType * @param buf the memory buffer containing the bytes. - * @return the component containing the byte at the given offset or null if no - * component defined. + * @return the first component containing the byte at the given offset or null if no + * component defined. A zero-length component may be returned. */ public final DataTypeComponent getComponentAt(int offset, MemBuffer buf) { + // TODO: This interface should be consistent with Structure DataTypeComponent[] comps = getComps(buf); if (comps == null) { return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java index 0f2a92f11f..129a820b7c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java @@ -44,48 +44,71 @@ public interface Structure extends Composite { public DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException; /** - * Gets the first immediate child component located at or after the given offset. + * Gets the first defined component located at or after the specified offset. + * Note: The returned component may be a zero-length component. * * @param offset the byte offset into this structure - * @return the immediate child component located at or after the given offset or null if not found. + * @return the first defined component located at or after the specified offset or null if not found. */ public DataTypeComponent getDefinedComponentAtOrAfterOffset(int offset); /** - * Gets the first immediate child component that contains the byte at the given offset. + * Gets the first non-zero-length component that contains the byte at the specified offset. * Note that one or more components may share the same offset when a bit-field or zero-length - * component is present since these may share an offset. + * component is present since these may share an offset. A null may be returned under one of + * the following conditions: + *
    + *
  • offset only corresponds to a zero-length component
  • + *
  • offset corresponds to a padding byte within a packed structure
  • + *
  • offset is >= structure length.
  • + *
* * @param offset the byte offset into this structure - * @return the first immediate child component containing offset or null if not found. + * @return the first non-zero-length component that contains the byte at the specified offset + * or null if not found. */ public DataTypeComponent getComponentContaining(int offset); /** - * Gets the first immediate defined child component that contains the byte at the given offset. + * Gets the first non-zero-length child component that starts at the specified offset. * Note that one or more components may share the same offset when a bit-field or zero-length - * component is present since these may share an offset. + * component is present since these may share an offset. A null may be returned under one of + * the following conditions: + *
    + *
  • offset only corresponds to a zero-length component
  • + *
  • offset corresponds to a padding byte within a packed structure
  • + *
  • offset corresponds to a component but is not the starting offset of that component
  • + *
  • offset is >= structure length
  • + *
* * @param offset the byte offset into this structure - * @return the first immediate child component containing offset or null if not found. - * @deprecated method name has been changed to better reflect behavior. The method - * {@link #getComponentContaining(int)} should be used instead. When switching over - * it may be a could time to verify that the caller can handle the possibility of multiple - * components containing the specified offset due to the possible presence of zero-length - * components, such as zero-element arrays, or bit-fields which can overlap at the - * byte-level. + * @return the first component that starts at specified offset or null if not found. */ public default DataTypeComponent getComponentAt(int offset) { - return getComponentContaining(offset); + DataTypeComponent dtc = getComponentContaining(offset); + if (dtc != null && dtc.getOffset() == offset) { + return dtc; + } + return null; } /** - * Get an ordered list of immediate child components that contain the byte at the given offset. - * Note that this will only return more than one component when a bit-field or zero-length - * component is present since these may share an offset. + * Get an ordered list of components that contain the byte at the specified offset. + * Unlike {@link #getComponentAt(int)} and {@link #getComponentContaining(int)} this method will + * include zero-length components if they exist at the specified offset. For this reason the + * specified offset may equal the structure length to obtain and trailing zero-length components. + * Note that this method will only return more than one component when a bit-fields and/or + * zero-length components are present since these may share an offset. An empty list may be + * returned under the following conditions: + *
    + *
  • offset corresponds to a padding byte within a packed structure
  • + *
  • offset corresponds to a component but is not the starting offset of that component
  • + *
  • offset is equal structure length and no trailing zero-length components exist
  • + *
  • offset is > structure length
  • + *
* * @param offset the byte offset into this structure - * @return a list of zero or more child components containing the specified offset + * @return a list of zero or more components containing the specified offset */ public List getComponentsContaining(int offset); @@ -256,7 +279,7 @@ public interface Structure extends Composite { * components may be cleared. This method will preserve the structure length and placement * of other components since freed space will appear as undefined components. *

- * To avoid clearing zero-length components at a given offset within a non-packed structure, + * To avoid clearing zero-length components at a specified offset within a non-packed structure, * the {@link #replaceAtOffset(int, DataType, int, String, String)} may be used with to clear * only the sized component at the offset by specified {@link DataType#DEFAULT} as the replacement * datatype. @@ -266,7 +289,7 @@ public interface Structure extends Composite { public void clearAtOffset(int offset); /** - * Clears the defined component at the given component ordinal. Clearing a component within + * Clears the defined component at the specified component ordinal. Clearing a component within * a non-packed structure causes a defined component to be replaced with a number of undefined * components. This may not the case when clearing a zero-length component or bit-field * which may not result in such undefined components. In the case of a packed structure @@ -397,7 +420,7 @@ public interface Structure extends Composite { String comment) throws IllegalArgumentException; /** - * Increases the size of the structure by the given amount by adding undefined filler at the + * Increases the size of the structure by the specified amount by adding undefined filler at the * end of the structure. NOTE: This method only has an affect on non-packed structures. * * @param amount the amount by which to grow the structure. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java index 3d2bc3d1d3..faf215041e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java @@ -179,9 +179,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (index >= 0) { // return first matching defined component containing offset DataTypeComponent dtc = components.get(index); - index = backupToFirstComponentContainingOffset(index, offset); + index = indexOfFirstNonZeroLenComponentContainingOffset(index, offset); dtc = components.get(index); - return dtc; + if (dtc.getLength() != 0) { + return dtc; + } + index = -index - 1; } if (offset != structLength && !isPackingEnabled()) { @@ -828,8 +831,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } /** - * Backup from specified ordinal to the first component which contains the specified offset. - * @param index component ordinal + * Backup from specified defined-component index to the first component which contains the specified offset. + * @param index any defined component index which contains offset * @param offset offset within structure * @return index of first defined component containing specific offset. */ @@ -848,8 +851,28 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } /** - * Advance from specified ordinal to the last component which contains the specified offset. - * @param index defined component index + * Identify defined-component index of the first non-zero-length component which contains the specified offset. + * If only zero-length components exist, the last zero-length component which contains the offset will be returned. + * @param index any defined component index which contains offset + * @param offset offset within structure + * @return index of first defined component containing specific offset. + */ + private int indexOfFirstNonZeroLenComponentContainingOffset(int index, int offset) { + index = backupToFirstComponentContainingOffset(index, offset); + DataTypeComponentImpl next = components.get(index); + while (next.getLength() == 0 && index < (components.size() - 1)) { + next = components.get(index + 1); + if (!next.containsOffset(offset)) { + break; + } + ++index; + } + return index; + } + + /** + * Advance from specified defined-component index to the last component which contains the specified offset. + * @param index any defined component index which contains offset * @param offset offset within structure * @return index of last defined component containing specific offset. */ diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java index 59654e57c4..a9a34abf62 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java @@ -1753,12 +1753,159 @@ public class StructureDBTest extends AbstractGTest { assertEquals(0, s.getNumComponents()); } + @Test + public void testGetComponentAt() { + /** + * /TestStruct + * pack(disabled) + * Structure TestStruct { + * 0 byte 1 field1 "Comment1" + * 1 word 2 null "Comment2" + * 3 dword 4 field3 "" + * 7 byte 1 field4 "Comment4" + * } + * Size = 8 Actual Alignment = 1 + */ + + DataTypeComponent dtc = struct.getComponentAt(3); + assertEquals(" 2 3 dword 4 field3 null", dtc.toString()); + + dtc = struct.getComponentAt(4); // offcut + assertNull(dtc); + + assertEquals(8, struct.getLength()); + + dtc = struct.getComponentAt(8); + assertNull(dtc); + + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null); + struct.add(new LongDataType(), "field4", null); + struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null); + + assertEquals(12, struct.getLength()); + + /** + * /TestStruct + * pack(disabled) + * Structure TestStruct { + * 0 byte 1 field1 "Comment1" + * 1 word 2 null "Comment2" + * 3 dword 4 field3 "" + * 7 byte 1 field4 "Comment4" + * 8 char[0] 0 zarray1 "" + * 8 long 4 field4 "" + * 12 long[0] 0 zarray2 "" + * } + * Size = 12 Actual Alignment = 1 + */ + + dtc = struct.getComponentAt(8); + assertEquals(" 5 8 long 4 field4 null", dtc.toString()); + + dtc = struct.getComponentAt(9); // offcut + assertNull(dtc); + + dtc = struct.getComponentAt(12); // end-of-struct + assertNull(dtc); + + // force components to align + struct.setPackingEnabled(true); + + /** + * /Test + * pack(disabled) + * Structure Test { + * 0 byte 1 field1 "Comment1" + * 2 word 2 null "Comment2" + * 4 dword 4 field3 "" + * 8 byte 1 field4 "Comment4" + * 9 char[0] 0 zarray1 "" + * 12 long 4 field4 "" + * 16 long[0] 0 zarray2 "" + * } + * Size = 16 Actual Alignment = 1 + */ + + assertEquals(16, struct.getLength()); + + dtc = struct.getComponentAt(9); // offset of zero-length component + assertNull(dtc); + + struct.setPackingEnabled(false); + + dtc = struct.getComponentAt(9); // undefined at offset of zero-length component + assertEquals(" 5 9 undefined 1 null null", dtc.toString()); + + } + @Test public void testGetComponentContaining() { DataTypeComponent dtc = struct.getComponentContaining(4); - assertEquals(DWordDataType.class, dtc.getDataType().getClass()); - assertEquals(2, dtc.getOrdinal()); - assertEquals(3, dtc.getOffset()); + assertEquals(" 2 3 dword 4 field3 null", dtc.toString()); + + assertEquals(8, struct.getLength()); + + dtc = struct.getComponentContaining(8); + assertNull(dtc); + + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null); + struct.add(new LongDataType(), "field4", null); + struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null); + + assertEquals(12, struct.getLength()); + + /** + * /TestStruct + * pack(disabled) + * Structure TestStruct { + * 0 byte 1 field1 "Comment1" + * 1 word 2 null "Comment2" + * 3 dword 4 field3 "" + * 7 byte 1 field4 "Comment4" + * 8 char[0] 0 zarray1 "" + * 8 long 4 field4 "" + * 12 long[0] 0 zarray2 "" + * } + * Size = 12 Actual Alignment = 1 + */ + + dtc = struct.getComponentContaining(8); + assertEquals(" 5 8 long 4 field4 null", dtc.toString()); + + dtc = struct.getComponentContaining(9); // offcut + assertEquals(" 5 8 long 4 field4 null", dtc.toString()); + + dtc = struct.getComponentContaining(12); // end-of-struct + assertNull(dtc); + + // force components to align + struct.setPackingEnabled(true); + + /** + * /Test + * pack(disabled) + * Structure Test { + * 0 byte 1 field1 "Comment1" + * 2 word 2 null "Comment2" + * 4 dword 4 field3 "" + * 8 byte 1 field4 "Comment4" + * 9 char[0] 0 zarray1 "" + * 12 long 4 field4 "" + * 16 long[0] 0 zarray2 "" + * } + * Size = 16 Actual Alignment = 1 + */ + + assertEquals(16, struct.getLength()); + + dtc = struct.getComponentContaining(9); // offset of zero-length component + assertNull(dtc); + + struct.setPackingEnabled(false); + + dtc = struct.getComponentContaining(9); // undefined at offset of zero-length component + assertEquals(" 5 9 undefined 1 null null", dtc.toString()); + } @Test diff --git a/Ghidra/Test/IntegrationTest/src/test/java/ghidra/program/database/data/DataDBTest.java b/Ghidra/Test/IntegrationTest/src/test/java/ghidra/program/database/data/DataDBTest.java index ff7acef630..960422580a 100644 --- a/Ghidra/Test/IntegrationTest/src/test/java/ghidra/program/database/data/DataDBTest.java +++ b/Ghidra/Test/IntegrationTest/src/test/java/ghidra/program/database/data/DataDBTest.java @@ -142,9 +142,9 @@ public class DataDBTest extends AbstractGenericTest { assertNotNull(c); assertEquals("c1", c.getComponentPathName()); - Data c2 = c.getComponentContaining(4); + Data c2 = c.getComponentContaining(4); // zero-length component is ignored assertNotNull(c2); - assertEquals("c1.", c2.getComponentPathName()); // zero-bitfield has no name + assertEquals("c1.bf1", c2.getComponentPathName()); Data c3 = c.getComponentContaining(5); assertNotNull(c3);