diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataAdapter.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataAdapter.java index 19fc5597a7..8ad8d89007 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataAdapter.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDefinedDataAdapter.java @@ -116,8 +116,14 @@ public interface DBTraceDefinedDataAdapter extends DBTraceDataAdapter { } } + @Deprecated @Override default DBTraceDefinedDataAdapter getComponentAt(int offset) { + return getComponentContaining(offset); + } + + @Override + default DBTraceDefinedDataAdapter getComponentContaining(int offset) { // We may write to the cache try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) { if (offset < 0 || offset >= getLength()) { @@ -133,7 +139,7 @@ public interface DBTraceDefinedDataAdapter extends DBTraceDataAdapter { } if (baseDataType instanceof Structure) { Structure structure = (Structure) baseDataType; - DataTypeComponent dtc = structure.getComponentAt(offset); + DataTypeComponent dtc = structure.getComponentContaining(offset); return dtc == null ? null : getComponent(dtc.getOrdinal()); } if (baseDataType instanceof Union) { @@ -155,7 +161,6 @@ public interface DBTraceDefinedDataAdapter extends DBTraceDataAdapter { if (offset < 0 || offset >= getLength()) { return null; } - DataType baseDataType = getBaseDataType(); if (baseDataType instanceof Array) { Array array = (Array) baseDataType; @@ -163,24 +168,30 @@ public interface DBTraceDefinedDataAdapter extends DBTraceDataAdapter { int index = offset / elementLength; return Collections.singletonList(getComponent(index)); } - if (baseDataType instanceof Structure) { - Structure structure = (Structure) baseDataType; - DataTypeComponent dtc = structure.getComponentAt(offset); + else if (baseDataType instanceof Structure) { + Structure struct = (Structure) baseDataType; List result = new ArrayList<>(); - // Logic handles overlapping bit fields + for (DataTypeComponent dtc : struct.getComponentsContaining(offset)) { + result.add(getComponent(dtc.getOrdinal())); + } + return result; + } + else if (baseDataType instanceof DynamicDataType) { + DynamicDataType ddt = (DynamicDataType) baseDataType; + DataTypeComponent dtc = ddt.getComponentAt(offset, this); + List result = new ArrayList<>(); + // Logic handles overlapping bit-fields // Include if offset is contained within bounds of component - while (dtc != null && offset >= dtc.getOffset() && - offset <= dtc.getOffset() + dtc.getLength() - 1) { - int ordinal = dtc.getOrdinal(); // TODO: Seems I could move this before while - result.add(getComponent(ordinal)); - ordinal++; - dtc = ordinal < structure.getNumComponents() ? structure.getComponent(ordinal) + while (dtc != null && (offset >= dtc.getOffset()) && + (offset < (dtc.getOffset() + dtc.getLength()))) { + int ordinal = dtc.getOrdinal(); + result.add(getComponent(ordinal++)); + dtc = ordinal < ddt.getNumComponents(this) ? ddt.getComponent(ordinal, this) : null; } return result; } - if (baseDataType instanceof Union) { - /** NOTE: The {@link DataDB} implementation seems hasty */ + else if (baseDataType instanceof Union) { Union union = (Union) baseDataType; List result = new ArrayList<>(); for (DataTypeComponent dtc : union.getComponents()) { @@ -190,12 +201,6 @@ public interface DBTraceDefinedDataAdapter extends DBTraceDataAdapter { } return result; } - if (baseDataType instanceof DynamicDataType) { - DynamicDataType dynamic = (DynamicDataType) baseDataType; - DataTypeComponent dtc = dynamic.getComponentAt(offset, this); - return dtc == null ? Collections.emptyList() - : Collections.singletonList(getComponent(dtc.getOrdinal())); - } return Collections.emptyList(); } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java index cf549014be..c308e12ea8 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java @@ -237,6 +237,11 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey return null; } + @Override + public Data getComponentContaining(int offset) { + return null; + } + @Override public List getComponentsContaining(int offset) { if (offset < 0 || offset >= getLength()) { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java index 95dd3597ff..fda9c111bf 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/listing/DBTraceCodeUnitTest.java @@ -1414,8 +1414,8 @@ public class DBTraceCodeUnitTest extends AbstractGhidraHeadlessIntegrationTest assertNull(u3fff.getFieldName()); assertNull(s4000.getFieldName()); assertEquals("nuD", s4000nuD.getFieldName()); - assertEquals("field_0x4", s4000lE.getFieldName()); - assertEquals("field_0x8", s4000pF.getFieldName()); + assertEquals("field2_0x4", s4000lE.getFieldName()); + assertEquals("field3_0x8", s4000pF.getFieldName()); // TODO: DAT... may change when proper symbols are implemented assertEquals("DAT_00003fff", u3fff.getPathName()); diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/data/DBDomainObjectSupport.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/data/DBDomainObjectSupport.java index c3d0fbbf07..899af3167f 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/data/DBDomainObjectSupport.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/data/DBDomainObjectSupport.java @@ -85,6 +85,10 @@ public abstract class DBDomainObjectSupport extends DomainObjectAdapterDB { } catch (VersionException e) { versionExc = e.combine(versionExc); + + // TODO: (see GP-1238) Consider properly supporting VersionException and upgrades. + // Returning a null manager will likely induce an NPE down the line. + return null; } } diff --git a/Ghidra/Features/Base/data/pcodetest/EmuTesting.gdt b/Ghidra/Features/Base/data/pcodetest/EmuTesting.gdt index 84a232e99c..bcc5b355a1 100644 Binary files a/Ghidra/Features/Base/data/pcodetest/EmuTesting.gdt and b/Ghidra/Features/Base/data/pcodetest/EmuTesting.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt index c88bc41d43..d608165ec7 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt and b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt index fc0cacd3b1..fa4ad9f9cf 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt and b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt b/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt index a3c09e5af0..6b29e704cf 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt and b/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt index ba023c6bd0..a435ea98d4 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt and b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt index 0494486013..e3caed2c91 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt and b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm index f3be8ce6b7..69c6be97ae 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm @@ -373,28 +373,22 @@

Note:The use of bitfield components is not currently reflected in decompiler results or assembly markup.

-

Flexible Array Component

+

Zero-Element / Flexible Array Components

-

A structure may be defined with a trailing flexible array component which corresponds to - an unsized array (e.g., char[0]). Such a component, if it exists, is always displayed as the - last row within the structure editor with the open edit row for additional components located - directly above it. The DataType column for a flexible array always shows - the base type of the unsized array which can be modified similar to other components. A - flexible array is added to the end of a structure by adding a last row specified by the base - DataType (e.g. char) - then invoking the array action and specifying an element count of 0. Attempting to specify - an element count of 0 is only permitted on the last component row when a flexible array component - does not already exist. The presence of a flexible array component does not affect the size of a - structure and will not appear within the structure when applied to Data within memory as it - corresponds to the memory location which immediately follows the end of the structure.

- +

A structure may be defined with zero-element array components, also referred to as a + flexible array, which correspond to an unsized array (e.g., char[0]). Such a component + is created by simply specifying a zero-element array datatype as you would a sized-array component + (see Create + Zero-Element / Flexible Array Component). + The resulting component will not consume any space within the structure and will report a component + size of 0. As such, it may reside at the same offset as a subsequent component and/or may have an offset + equal to the length of the structure. When packing is enabled such a component may influence the + overall alignment of the structure and its length. +

Note:The use of flexible array components is not currently reflected in decompiler results or listing reference markup. Its primary purpose if to reflect the C/C++ source definition of a structure with correct alignment and structure sizing.

-

Note:While C/C++ support flexible arrays anywhere - within a structure, Ghidra only supports the case where it is the last structure component.

-


@@ -415,7 +409,7 @@

Within a non-packed structure undefined components are inserted before the current selection by clicking the Insert Undefined Byte button. Within a packed structure an undefined1 datatype component + "images/Plus.png" alt=""> Insert Undefined Byte button. Within a packed structure an undefined1 datatype component is inserted in a similar fashion, although in packed structures it is more appropriate to use a properly sized datatype (e.g., modify datatype on inserted component).

@@ -587,24 +581,14 @@

Create - Flexible Array

+ Zero-Element / Flexible Array Component
-

To create an unsized flexible array as the last structure component (assuming one - is not already defined):

- -
    -
  1. Add a component whose DataType corresponds to the base type of the unsized array.
  2. - -
  3. Select this last structure component in the table.
  4. - -
  5. Press the Create Array button or the '[' - action key binding.
  6. - -
  7. A dialog pops up to request - the number of elements in the array. Specify 0 as the number of elements in the popup - dialog and click OK.
  8. -
+

To create an unsized flexible array component you must use the datatype cell edit feature + to select/enter the datatype by name including the array-sizing-specification (e.g., char[0]). + To initiate edit mode for a component datatype you must double-click the datatype cell for the component + you wish to modify. This may be an existing component or the empty row at the end of the table. + See Editing the DataType Field for more details.

@@ -617,8 +601,7 @@ button. For example, the array component Word[4] would become four Word components. If the structure struct_1 was composed of a Float and a DWord, then unpackaging it would replace the struct_1 component with a Float component and a DWord component.

-
- +

Create Structure From Selection

@@ -778,49 +761,81 @@ session top to bottom in the table.

-

Editing the DataType Field

+

Editing the DataType Field

-

This can be any data type that is available in the data type manager. To edit the data - type double-click the data type cell. This will show the To edit the data type double-click the data type cell. This will show the Data Type Selection Dialog, - which allows you to easily enter a data type. It can also be Undefined, a pointer to a data - type, or an array.

+ which allows you to easily enter a data type or select/modify an existing one from one of the open + datatype managers. It can also be Undefined, a pointer to a datatype, or an array.

+ +

Note: Certain restrictions may apply when specifying + or modifying a component's datatype which could cause a modification to fail. Some common restrictions include + but are not limited to:

+ +
+
    +
  • Fit (non-packed structure only) - when modifying a component + there must be adequate space to place the component. Any undefined components which immediately follow + the modified component may be consumed or added based upon the component size change. It may be neccessary to + insert undefined bytes or clear other components before the modification may be completed.
  • +
  • Illegal Datatype - certain datatypes are not permitted (e.g., data driven Dynamic types, Factory types).
  • +
  • Zero-Length Components (non-packed structure only) - modification of an existing zero-length component may + not change its size.
  • +
+
+ +

Examples of datatypes which may be specified, include:

-

Basic Data Type

+

Basic Data Type

-

This can be a built-in data type (Byte, Word, etc.), a structure, a union, or a - typedef.

- -

For example, Word.

+

This can be any fixed-length built-in data type (e.g., byte, word, etc.), structure, union, typedef, or enum. + +

Sizable Dynamic Data Type

+ +

A sizable built-in Dynamic data type such as string may be specified in which case the user + will be prompted to specify the component size.

Pointer Data Type

-

This can be the basic Pointer data type or a pointer to a data type. A pointer to a data - type is indicated by following the data type with an *.

+

This can be a named Pointer data type (e.g., pointer, pointer16, etc.) or a pointer to a specific data type. A pointer to a data + type is indicated by following the data type name with an * (i.e., asterisk symbol). It is also possible to stipulate a specific pointer-size, although it should be + only done when the default pointer size is inappropriate. A stipulated pointer size may be specified by following the * with its bit-size + (16,24,32..64). The pointer * designation may also be stacked for a pointer-to-pointer type.

-

For example, Word * is a pointer to a Word.

+

For example, word* is a pointer to a Word using the default pointer size. If a non-default size is needed word*16 would force + the use of a 16-bit pointer.

Array

-

This can be a multidimensional array of any data type.

+

A single or multidimensional array of any basic data type may be specified.

-

For example, DWord[2][4] is an array with 2 elements, where each element is an array - with 4 elements that are DWords.

+

For example, dword[2][4] is an array with 2 elements, where each element is an array + with 4 elements that are DWords; In a similar fashion a zero-element character array may be specified + as char[0].

Array of Pointers

-

Arrays of pointers can also be specified.

+

Arrays of pointers may also be specified.

-

For example, Float*[5] is an array with five elements where each element is a pointer to +

For example, float*[5] is an array with five elements where each element is a pointer to a Float.

- + +

Bitfields

+ +

A bitfield may be specified by appending :<bitsize> to the end of a valid base datatype. Valid base datatypes include + all integer types (e.g., byte, char, int, etc.), a defined enum, or a typedef which corresponds to an integer-type or enum. + It is important to note that specifying a bitfield in this manner works well for packed structures, however for non-packed + structures it may be neccessary to use the Bitfield Actions to properly define a bitfield.

+ +

For example, int:4 defines a signed integer bitfield which is 4-bits in length. +

Effect of Changing a Component's Size

A non-packed union's size will always be the size of its largest component. If you change a data type for a component and the component size changes, the union size will - change if necessary. A packed union is padded to make its size a multiple of the union's + change if necessary. A packed union is padded to make its size a multiple of the union's alignment.

diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png index 3ab2ea0a87..8fcb4e4528 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayCmd.java index bcaa25a48b..c49df702b5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayCmd.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,9 +21,10 @@ import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.Msg; /** - * Command to create an array. + * Command to create an array. All conflicting data will be cleared. * */ public class CreateArrayCmd implements Command { @@ -37,9 +37,11 @@ public class CreateArrayCmd implements Command { /** * Constructs a new command for creating arrays. * @param addr The address at which to create an array. - * @param numElements the number of elements in the array to be created. + * @param numElements the number of elements in the array to be created. + * A 0 element count is permitted but a minimum length will apply for all array instances. * @param dt the dataType of the elements in the array to be created. - * @param elementLength the size of an element in the array. + * @param elementLength the size of an element in the array. Only used for Dynamic + * datatype dt when {@link Dynamic#canSpecifyLength()} returns true. */ public CreateArrayCmd(Address addr, int numElements, DataType dt, int elementLength) { this.addr = addr; @@ -47,69 +49,47 @@ public class CreateArrayCmd implements Command { this.dataType = dt; this.elementLength = elementLength; } - - /** - * - * @see ghidra.framework.cmd.Command#applyTo(ghidra.framework.model.DomainObject) - */ + + @Override public boolean applyTo(DomainObject obj) { Program program = (Program)obj; Listing listing = program.getListing(); - - if (dataType instanceof FactoryDataType) { - msg = "Array not allowed on a Factory data-type: " + dataType.getName(); - return false; - } - if (dataType instanceof Dynamic && !((Dynamic)dataType).canSpecifyLength()) { - msg = "Array not allowed on a non-sizable Dynamic data-type: " + dataType.getName(); - return false; - } - if (elementLength <=0) { - msg = "DataType must have fixed size > 0, not "+dataType.getLength(); - return false; - } - if (numElements <= 0) { - msg = "Number of elements must be positive, not "+numElements; - return false; - } - - int length = numElements*elementLength; - Address endAddr; try { - endAddr = addr.addNoWrap(length - 1); + ArrayDataType adt = new ArrayDataType(dataType, numElements, elementLength, + program.getDataTypeManager()); + int length = adt.getLength(); + + Address endAddr = addr.addNoWrap(length - 1); + AddressSet set = new AddressSet(addr, endAddr); + InstructionIterator iter = listing.getInstructions(set, true); + if (iter.hasNext()) { + msg = "Can't create data because the current selection contains instructions"; + return false; + } + listing.clearCodeUnits(addr, endAddr, false); + listing.createData(addr, adt, adt.getLength()); } catch (AddressOverflowException e1) { msg = "Can't create data because length exceeds address space"; return false; } - AddressSet set = new AddressSet(addr, endAddr); - InstructionIterator iter = listing.getInstructions(set, true); - if (iter.hasNext()) { - msg = "Can't create data because the current selection contains instructions"; - return false; - } - - listing.clearCodeUnits(addr, endAddr, false); - ArrayDataType adt = new ArrayDataType(dataType, numElements, elementLength); - try { - listing.createData(addr, adt, adt.getLength()); - } catch(CodeUnitInsertionException e) { + catch (IllegalArgumentException | CodeUnitInsertionException e) { msg = e.getMessage(); return false; } + catch (RuntimeException e) { + msg = "Unexpected error: " + e.toString(); + Msg.error(this, msg, e); + return false; + } return true; } - - /** - * @see ghidra.framework.cmd.Command#getStatusMsg() - */ + @Override public String getStatusMsg() { return msg; } - /** - * @see ghidra.framework.cmd.Command#getName() - */ + @Override public String getName() { return "Create Array"; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java index db26d3d1dc..c80ef3c6c1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java @@ -20,9 +20,11 @@ import ghidra.framework.model.DomainObject; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; +import ghidra.util.Msg; /** - * Command to create an array inside of a structure. + * Command to create an array inside of a structure. All conflicting components + * within the targeted structure will be replaced with the new array component. * */ public class CreateArrayInStructureCmd implements Command { @@ -32,14 +34,21 @@ public class CreateArrayInStructureCmd implements Command { private DataType dataType; private int[] compPath; + // NOTE: This command does not currently handle Dynamic types whose length may + // be specified since no elementLength parameter exists. + /** * Constructs a new command for creating arrays inside of structures. + * The specified component will be replaced as will subsequent components within + * the structure required to make room for the new array component. + * NOTE: This is intended for replacing existing components and not for + * simply inserting an array component. * @param addr The address of the structure that will contain the new array. - * @param numElements the number of elements in the array to be created. + * @param numElements the number of elements in the array to be created. A 0 element count is permitted. * @param dt the dataType of the elements in the array to be created. - * @param compPath the component path within the structure at which to create - * the array. The component path is an array of integers where each integer - * is a component index of the component above it. + * @param compPath the target component path within the structure of an existing component where + * the array should be created. The component path is an array of integers where each integer + * is a component index of the component above it. */ public CreateArrayInStructureCmd(Address addr, int numElements, DataType dt, int[] compPath) { @@ -48,42 +57,18 @@ public class CreateArrayInStructureCmd implements Command { this.dataType = dt; this.compPath = compPath; } - /** - * - * @see ghidra.framework.cmd.Command#applyTo(ghidra.framework.model.DomainObject) - */ + + @Override public boolean applyTo(DomainObject obj) { Program program = (Program)obj; Listing listing = program.getListing(); - - if (dataType instanceof FactoryDataType) { - msg = "Array not allowed on a Factory data-type: " + dataType.getName(); - return false; - } - if (dataType instanceof Dynamic && !((Dynamic)dataType).canSpecifyLength()) { - msg = "Array not allowed on a non-sizable Dynamic data-type: " + dataType.getName(); - return false; - } - if (numElements <= 0) { - msg = "Number of elements must be positive, not "+numElements; - return false; - } Data data = listing.getDataContaining(addr); Data compData = data.getComponent(compPath); - - int elementLength; - if (dataType instanceof Dynamic) { - elementLength = compData.getLength(); - } - else { - elementLength = dataType.getLength(); - } - if (elementLength <=0) { - msg = "DataType must have fixed size > 0, not "+elementLength; + if (compData == null) { + msg = "Invalid target component path specified"; return false; } - int length = numElements*elementLength; int index = compData.getComponentIndex(); int offset = compData.getParentOffset(); @@ -94,23 +79,34 @@ public class CreateArrayInStructureCmd implements Command { return false; } Structure struct = (Structure)parentDataType; - if (offset+length > struct.getLength()) { - msg = "Array too big for structure"; - return false; - } + + DataType baseDt = dataType; + if (dataType instanceof TypeDef) { + baseDt = ((TypeDef) dataType).getBaseDataType(); + } + if (baseDt instanceof Dynamic) { + msg = "Dynamic data-type may not be specified: " + dataType.getName(); + return false; + } try { ArrayDataType adt = new ArrayDataType(dataType, numElements, dataType.getLength()); + int length = adt.isZeroLength() ? 0 : adt.getLength(); + if (!struct.isPackingEnabled() && (offset + length) > struct.getLength()) { + msg = "Array too big for structure"; + return false; + } clearStruct(struct, compData.getParentOffset(), length); if (struct.isPackingEnabled()) { - struct.insert(index, adt, adt.getLength()); + struct.insert(index, adt, -1); } else { - struct.replace(index, adt, adt.getLength()); + struct.replace(index, adt, -1); } } - catch(Exception e) { - msg = e.getMessage(); + catch (RuntimeException e) { + msg = "Unexpected error: " + e.toString(); + Msg.error(this, msg, e); return false; } return true; @@ -129,6 +125,7 @@ public class CreateArrayInStructureCmd implements Command { /** * @see ghidra.framework.cmd.Command#getStatusMsg() */ + @Override public String getStatusMsg() { return msg; } @@ -136,6 +133,7 @@ public class CreateArrayInStructureCmd implements Command { /** * @see ghidra.framework.cmd.Command#getName() */ + @Override public String getName() { return "Create Array"; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java index f392cdbfdf..33b2244b55 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java @@ -1094,62 +1094,6 @@ public class DataTypeMergeManager implements MergeResolver { } } - private void updateFlexArray(long sourceDtID, Structure sourceDt, Structure destStruct, - Map resolvedDataTypes) { - - DataTypeComponent flexDtc = sourceDt.getFlexibleArrayComponent(); - if (flexDtc == null) { - return; - } - - DataTypeManager sourceDTM = sourceDt.getDataTypeManager(); - - DataType sourceCompDt = flexDtc.getDataType(); - String comment = flexDtc.getComment(); - long sourceComponentID = sourceDTM.getID(sourceCompDt); - - // Try to get a mapping of the source data type to a result data type. - DataType resultCompDt = getResolvedComponent(sourceComponentID, resolvedDataTypes); - - if (resultCompDt == null) { - // We didn't have a map entry for the data type. - - if (!myDtAddedList.contains(Long.valueOf(sourceComponentID))) { - // Not added so should be in result if it wasn't deleted there. - DataType rDt = dtms[RESULT].getDataType(sourceComponentID); - if (rDt != null) { - resultCompDt = rDt; - } - } - if (resultCompDt == null) { - // Not added/resolved yet - // put an entry in the fixup list - fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID, Integer.MAX_VALUE, - resolvedDataTypes)); - fixUpIDSet.add(sourceDtID); - - // substitute datatype to preserve component name for subsequent fixup - resultCompDt = Undefined1DataType.dataType; - } - } - try { - // Apply resultCompDt as flex array - try { - destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(), comment); - } - catch (IllegalArgumentException e) { - displayError(destStruct, e); - DataType badDt = Undefined1DataType.dataType; - comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " + - e.getMessage() + " " + ((comment != null) ? (" " + comment) : ""); - destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment); - } - } - catch (IllegalArgumentException e) { - displayError(destStruct, e); - } - } - private void updateStructure(long sourceDtID, Structure sourceDt, Structure destStruct, Map resolvedDataTypes) { @@ -1332,8 +1276,6 @@ public class DataTypeMergeManager implements MergeResolver { if (!aligned) { adjustStructureSize(destStruct, sourceDt.getLength()); } - - updateFlexArray(sourceDtID, sourceDt, destStruct, resolvedDataTypes); } /** @@ -1343,7 +1285,7 @@ public class DataTypeMergeManager implements MergeResolver { */ private static void adjustStructureSize(Structure struct, int preferredSize) { - DataTypeComponent dtc = struct.getComponentAt(preferredSize); + DataTypeComponent dtc = struct.getComponentContaining(preferredSize); if (dtc == null) { struct.growStructure(preferredSize - struct.getLength()); return; @@ -1895,16 +1837,6 @@ public class DataTypeMergeManager implements MergeResolver { } checkOffsets = true; } - DataTypeComponent flexDtc1 = ((Structure) c1).getFlexibleArrayComponent(); - DataTypeComponent flexDtc2 = ((Structure) c2).getFlexibleArrayComponent(); - if (flexDtc1 != null && flexDtc2 != null) { - if (isChangedComponent(flexDtc1, flexDtc2, dtm1, dtm2, false)) { - return true; - } - } - else if (flexDtc1 != null || flexDtc2 != null) { - return true; - } } DataTypeComponent[] c1Components = c1.getDefinedComponents(); @@ -2384,7 +2316,7 @@ public class DataTypeMergeManager implements MergeResolver { } /** - * Process fixup for aligned structure component or trailing flexible array + * Process fixup for aligned structure component * @param info fixup info * @param struct result structure * @param dt component datatype @@ -2392,35 +2324,15 @@ public class DataTypeMergeManager implements MergeResolver { */ private boolean fixUpAlignedStructureComponent(FixUpInfo info, Structure struct, DataType dt) { int ordinal = info.index; - boolean isFlexArrayFixup = (info.index == Integer.MAX_VALUE); DataTypeComponent dtc = null; - if (isFlexArrayFixup) { - dtc = struct.getFlexibleArrayComponent(); - } - else { - if (ordinal >= 0 || ordinal < struct.getNumComponents()) { - dtc = struct.getComponent(ordinal); - } + if (ordinal >= 0 || ordinal < struct.getNumComponents()) { + dtc = struct.getComponent(ordinal); } if (dtc == null) { return false; } - if (isFlexArrayFixup) { - try { - struct.setFlexibleArrayComponent(dt, dtc.getFieldName(), dtc.getComment()); - } - catch (IllegalArgumentException e) { - displayError(struct, e); - DataType badDt = Undefined1DataType.dataType; - String comment = dtc.getComment(); - comment = "Couldn't add " + dt.getDisplayName() + "[ ] here. " + e.getMessage() + - " " + ((comment != null) ? (" " + comment) : ""); - struct.replace(ordinal, badDt, dtc.getLength(), dtc.getFieldName(), comment); - struct.setFlexibleArrayComponent(badDt, dtc.getFieldName(), comment); - } - } - else if (dtc.isBitFieldComponent()) { + if (dtc.isBitFieldComponent()) { if (BitFieldDataType.isValidBaseDataType(dt)) { // replace bitfield base datatype - silent if updated type is not a valid base type BitFieldDataType bfDt = (BitFieldDataType) dtc.getDataType(); @@ -2542,13 +2454,10 @@ public class DataTypeMergeManager implements MergeResolver { DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht); - boolean isFlexArrayFixup = (info.index == Integer.MAX_VALUE); - if (compDt != null) { - if (struct.isPackingEnabled() || isFlexArrayFixup) { + if (struct.isPackingEnabled()) { if (!fixUpAlignedStructureComponent(info, struct, compDt)) { - String msg = - isFlexArrayFixup ? "flex-array component" : ("component " + info.index); + String msg = "component " + info.index; Msg.warn(this, "Structure Merge: Couldn't get " + msg + " in " + struct.getPathName() + " data type during fix up."); return false; // Don't remove this FixUpInfo from the fixupList so the user will get notified. @@ -2565,9 +2474,6 @@ public class DataTypeMergeManager implements MergeResolver { } // Datatype failed to resolved - check to see if we have a placeholder - else if (isFlexArrayFixup) { - struct.clearFlexibleArrayComponent(); - } else if (struct.isPackingEnabled()) { int ordinal = info.index; int numComponents = struct.getNumComponents(); @@ -3291,7 +3197,7 @@ public class DataTypeMergeManager implements MergeResolver { if (composite.isPackingEnabled() || (composite instanceof Union)) { return dtc.getOrdinal(); } - return dtc.getOffset(); + return dtc.getOffset(); // TODO: use of offset could be problematic with shared offset } /** @@ -3301,7 +3207,7 @@ public class DataTypeMergeManager implements MergeResolver { private class FixUpInfo { long id; long compID; - int index; + int index; // TODO: index as offset could be problematic with shared offset Map ht; // bitfield info @@ -3315,7 +3221,6 @@ public class DataTypeMergeManager implements MergeResolver { * @param compID id of either component or base type * @param index offset into non-packed structure, or ordinal into union or packed * structure; or parameter/return ordinal; for other data types index is not used (specify -1). - * For structure trailing flex-array specify {@link Integer#MAX_VALUE}. * @param resolvedDataTypes hashtable used for resolving the data type */ FixUpInfo(long id, long compID, int index, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java index ab746965c9..f2bbdd6e9b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java @@ -87,44 +87,44 @@ class DataTypePanel extends JPanel { pathAttrSet = new SimpleAttributeSet(); pathAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - pathAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + pathAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); pathAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); pathAttrSet.addAttribute(StyleConstants.Foreground, MergeConstants.CONFLICT_COLOR); nameAttrSet = new SimpleAttributeSet(); nameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - nameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + nameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); nameAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); sourceAttrSet = new SimpleAttributeSet(); sourceAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - sourceAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11)); + sourceAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(11)); sourceAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); sourceAttrSet.addAttribute(StyleConstants.Foreground, SOURCE_COLOR); offsetAttrSet = new SimpleAttributeSet(); offsetAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced"); - offsetAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12)); + offsetAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(12)); offsetAttrSet.addAttribute(StyleConstants.Foreground, Color.BLACK); contentAttrSet = new SimpleAttributeSet(); contentAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced"); - contentAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12)); + contentAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(12)); contentAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE); fieldNameAttrSet = new SimpleAttributeSet(); fieldNameAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced"); - fieldNameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12)); + fieldNameAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(12)); fieldNameAttrSet.addAttribute(StyleConstants.Foreground, new Color(204, 0, 204)); commentAttrSet = new SimpleAttributeSet(); commentAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced"); - commentAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12)); + commentAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(12)); commentAttrSet.addAttribute(StyleConstants.Foreground, new Color(0, 204, 51)); deletedAttrSet = new SimpleAttributeSet(); deletedAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma"); - deletedAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12)); + deletedAttrSet.addAttribute(StyleConstants.FontSize, Integer.valueOf(12)); deletedAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE); deletedAttrSet.addAttribute(StyleConstants.Foreground, Color.RED); @@ -149,7 +149,7 @@ class DataTypePanel extends JPanel { } private void formatAlignment(Composite composite) { - String str = CompositeDataTypeImpl.getAlignmentAndPackingString(composite); + String str = CompositeInternal.getAlignmentAndPackingString(composite); insertString(str + "\n\n", sourceAttrSet); } @@ -178,10 +178,7 @@ class DataTypePanel extends JPanel { DataType dt = dtc.getDataType(); StringBuilder buffer = new StringBuilder(); buffer.append(dt.getName()); - if (dtc.isFlexibleArrayComponent()) { - buffer.append("[0]"); - } - else if (dt instanceof BitFieldDataType && + if (dt instanceof BitFieldDataType && !((Composite) dtc.getParent()).isPackingEnabled()) { BitFieldDataType bfDt = (BitFieldDataType) dt; buffer.append("("); @@ -204,14 +201,9 @@ class DataTypePanel extends JPanel { offsetWidth += 2; // factor in 0x prefix String offsetStr = ""; if (offsetWidth > 0) { - if (!dtc.isFlexibleArrayComponent()) { - offsetStr = "0x" + Integer.toHexString(dtc.getOffset()); - offsetStr = StringUtilities.pad(offsetStr, ' ', offsetWidth - offsetStr.length()); - offsetStr += ": "; - } - else { - offsetStr = StringUtilities.pad(offsetStr, ' ', offsetWidth + 2); - } + offsetStr = "0x" + Integer.toHexString(dtc.getOffset()); + offsetStr = StringUtilities.pad(offsetStr, ' ', offsetWidth - offsetStr.length()); + offsetStr += ": "; insertString(" " + offsetStr + " ", offsetAttrSet); } fieldName = pad(fieldName, fieldNameWidth); @@ -233,10 +225,8 @@ class DataTypePanel extends JPanel { boolean showComponentOffset = false; DataTypeComponent[] components = comp.getDefinedComponents(); - DataTypeComponent flexDtc = null; if (comp instanceof Structure) { showComponentOffset = !comp.isPackingEnabled(); - flexDtc = ((Structure) comp).getFlexibleArrayComponent(); } int offsetLength = showComponentOffset ? Integer.toHexString(comp.getLength()).length() : 0; @@ -246,17 +236,10 @@ class DataTypePanel extends JPanel { maxDtNameLength = max(getDataTypeName(component), maxDtNameLength); maxFieldNameLength = max(component.getFieldName(), maxFieldNameLength); } - if (flexDtc != null) { - maxDtNameLength = max(getDataTypeName(flexDtc), maxDtNameLength); - maxFieldNameLength = max(flexDtc.getFieldName(), maxFieldNameLength); - } for (DataTypeComponent component : components) { renderComponent(component, maxDtNameLength, maxFieldNameLength, offsetLength); } - if (flexDtc != null) { - renderComponent(flexDtc, maxDtNameLength, maxFieldNameLength, offsetLength); - } insertString("}\n\n", contentAttrSet); insertAlignment(comp); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java index c6188c5ed9..8a7462ba70 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java @@ -47,7 +47,7 @@ public class AddBitFieldAction extends CompositeEditorTableAction { public void actionPerformed(ActionContext context) { CompEditorModel editorModel = (CompEditorModel) model; - if (editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) { + if (editorModel.getNumSelectedRows() != 1) { return; } int rowIndex = model.getSelectedRows()[0]; @@ -71,7 +71,7 @@ public class AddBitFieldAction extends CompositeEditorTableAction { CompEditorModel editorModel = (CompEditorModel) model; // Union do not support non-packed placement of bitfields if (!(editorModel.viewComposite instanceof Structure) || editorModel.isPackingEnabled() || - editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) { + editorModel.getNumSelectedRows() != 1) { enabled = false; } setEnabled(enabled); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java index 18d028a304..6d17bacfa2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java @@ -370,7 +370,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { void init(DataTypeComponent editDtc) { - if (editDtc == null || editDtc.isFlexibleArrayComponent()) { + if (editDtc == null) { editMode = EditMode.NONE; editOrdinal = -1; this.editComponent = null; @@ -401,6 +401,12 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { return false; } return bitFieldAllocation.hasConflict; + + // TODO: Improve conflict detection with zero-length components. + // Zero-length components can share common offset, although + // zero-length components should have a lower ordinal than a + // sized component at the same offset. + } /** @@ -603,7 +609,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { String conflictTip = "'" + conflict.getDataType().getDisplayName() + (conflictName != null ? (" " + conflictName) : "") + "' at offset " + conflict.getOffset(); - conflictMsg += "
conflict with " + + conflictMsg += "
overlaps " + HTMLUtilities.escapeHTML(conflictTip) + "
"; } return "
" + HTMLUtilities.escapeHTML(tip) + @@ -842,12 +848,22 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { BitFieldPlacement(DataTypeComponent component) { int startOffset = component.getOffset(); + int componentLength = component.getLength(); int offsetAdjBytes = startOffset - allocationByteOffset; if (!bigEndian) { - offsetAdjBytes = allocationByteSize - offsetAdjBytes - component.getLength(); + offsetAdjBytes = allocationByteSize - offsetAdjBytes - componentLength; } int leftAdj = 8 * offsetAdjBytes; - if (component.isBitFieldComponent()) { + if (componentLength == 0) { + // treat all zero-length fields the same + zeroBitField = true; + rightBit = leftAdj - 8; + if (!isBigEndian()) { + rightBit += 7; + } + leftBit = rightBit; + } + else if (component.isBitFieldComponent()) { BitFieldDataType bitfield = (BitFieldDataType) component.getDataType(); int storageSize = 8 * bitfield.getStorageSize(); rightBit = leftAdj + storageSize - bitfield.getBitOffset() - 1; @@ -855,13 +871,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { // bits stored (NOTE: this may cause a transition from declared to effective // bit-size when editing a bitfield where the these bit-sizes differ). int bitSize = bitfield.getBitSize(); - if (bitSize == 0) { - zeroBitField = true; - leftBit = rightBit; - } - else { - leftBit = rightBit - bitSize + 1; - } + leftBit = rightBit - bitSize + 1; } else { int componentSize = 8 * component.getLength(); @@ -1075,7 +1085,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { int allocationEndOffset = offset + allocationBytes - 1; int numComponents = struct.getNumComponents(); - DataTypeComponent component = struct.getComponentAt(offset); + DataTypeComponent component = struct.getDefinedComponentAtOrAfterOffset(offset); while (component != null) { if (component.getOffset() > allocationEndOffset) { break; @@ -1149,7 +1159,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { // determine placement attribute index within allocationBytes which // may have been reduced from allocationByteSize based upon visibility. - int index = bitIndex - (8 * rightChopBytes); + int index = bitIndex - (8 * leftChopBytes); if (index >= 0 && index < bitAttributes.length) { bitAttributes[index] = new BitAttributes(dtc, bitAttributes[index]); } @@ -1245,11 +1255,11 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { private DataTypeComponent getConflict() { BitAttributes c = conflict; - while (c != null && c.dtc.isZeroBitFieldComponent()) { + while (c != null && c.dtc.getLength() == 0 && c.conflict != null) { // TODO: improve conflict detection - // Zero-length bitfield could be conflict if placement is + // Zero-length components could be conflict if placement is // offcut with another component (currently ignored) - c = conflict.conflict; + c = c.conflict; } // NOTE: DEFAULT undefined datatype can be ignored as conflict return c != null && c.dtc.getDataType() != DataType.DEFAULT ? c.dtc : null; @@ -1301,7 +1311,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { } else { g.fillRect(rectangle.x, rectangle.y, bitWidth, CELL_HEIGHT); - if (conflict != null && conflict.dtc.isZeroBitFieldComponent()) { + if (conflict != null && conflict.dtc.getLength() == 0) { conflict.paint(g, null, false); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java index 392e0ba492..8a8c9ab7c7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java @@ -536,7 +536,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { if (isEditingField()) { endFieldEditing(); } - checkIsAllowableDataType(datatype, true); + checkIsAllowableDataType(datatype); if (length < 1) { DataTypeInstance dti = DataTypeHelper.getSizedDataType(getProvider(), datatype, lastNumBytes, Integer.MAX_VALUE); @@ -575,7 +575,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { endFieldEditing(); } - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); insertMultiple(rowIndex, dataType, dtLen, multiple, monitor); fixSelection(); componentEdited(); @@ -741,29 +741,23 @@ public abstract class CompEditorModel extends CompositeEditorModel { return null; } - checkIsAllowableDataType(datatype, !oldDtc.isFlexibleArrayComponent()); + checkIsAllowableDataType(datatype); - DataTypeComponent dtc; - if (oldDtc.isFlexibleArrayComponent()) { - // flexible array only supports fixed-length types - dtc = replace(rowIndex, datatype); - } - else { - int oldCompSize = oldDtc.getLength(); - int newCompSize = length; - int sizeDiff = newCompSize - oldCompSize; + int oldCompSize = oldDtc.getLength(); + int newCompSize = length; + int sizeDiff = newCompSize - oldCompSize; - // New one is larger so check to make sure it will fit. - if (sizeDiff > 0) { - if (!checkForReplace(rowIndex, datatype)) { - throw new InvalidDataTypeException(datatype.getDisplayName() + " doesn't fit."); - } + // New one is larger so check to make sure it will fit. + if (sizeDiff > 0) { + if (!checkForReplace(rowIndex, datatype)) { + throw new InvalidDataTypeException(datatype.getDisplayName() + " doesn't fit."); } - - // Replace the component at index. - dtc = replace(rowIndex, datatype, newCompSize, oldDtc.getFieldName(), - oldDtc.getComment()); } + + // Replace the component at index. + DataTypeComponent dtc = + replace(rowIndex, datatype, newCompSize, oldDtc.getFieldName(), oldDtc.getComment()); + fixSelection(); componentEdited(); selectionChanged(); @@ -793,11 +787,11 @@ public abstract class CompEditorModel extends CompositeEditorModel { } DataTypeComponent oldDtc = getComponent(startRowIndex); - if (oldDtc == null || oldDtc.isFlexibleArrayComponent()) { + if (oldDtc == null) { throw new AssertException(); } - checkIsAllowableDataType(datatype, true); + checkIsAllowableDataType(datatype); // // Note: if the range being replaced is large enough, then the UI could lock-up. If we @@ -839,7 +833,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { if (dtc == null) { return false; } - if (!isShowingUndefinedBytes() || dtc.isFlexibleArrayComponent()) { + if (!isShowingUndefinedBytes()) { return true; } // Does the new data type fit by replacing the component at index. @@ -869,10 +863,10 @@ public abstract class CompEditorModel extends CompositeEditorModel { protected abstract void replaceOriginalComponents(); @Override - protected void checkIsAllowableDataType(DataType datatype, boolean dynamicSizingAllowed) + protected void checkIsAllowableDataType(DataType datatype) throws InvalidDataTypeException { - super.checkIsAllowableDataType(datatype, dynamicSizingAllowed); + super.checkIsAllowableDataType(datatype); // Verify that we aren't adding this structure or anything that it is // part of to this editable structure. @@ -1021,12 +1015,6 @@ public abstract class CompEditorModel extends CompositeEditorModel { // Get data type to make into array. DataType dt = comp.getDataType(); - if (numElements == 0) { - // assume if 0 was permitted flexible array support has been provided - convertToFlexibleArray(rowIndex); - return; - } - ArrayDataType array = new ArrayDataType(dt, numElements, comp.getLength(), viewDTM); if (getNumSelectedComponentRows() > 1) { @@ -1244,17 +1232,17 @@ public abstract class CompEditorModel extends CompositeEditorModel { } @Override - public void setComponentDataTypeInstance(int rowIndex, DataTypeInstance dti) + public void setComponentDataTypeInstance(int rowIndex, DataType dt, int length) throws UsrException { if (getComponent(rowIndex) == null) { // Replacing data type in unlocked mode replaces only // that data type and structure size may change. - insert(rowIndex, dti.getDataType(), dti.getLength()); + insert(rowIndex, dt, length); } else { // Replacing data type in unlocked mode replaces only // that data type and structure size may change. - replace(rowIndex, dti.getDataType(), dti.getLength()); + replace(rowIndex, dt, length); } } @@ -1268,14 +1256,11 @@ public abstract class CompEditorModel extends CompositeEditorModel { @Override public void setComponentName(int rowIndex, String name) throws InvalidInputException, InvalidNameException, DuplicateNameException { - if (name.equals("")) { - name = null; - } if (nameExistsElsewhere(name, rowIndex)) { throw new InvalidNameException("Name \"" + name + "\" already exists."); } try { - getComponent(rowIndex).setFieldName(name); + getComponent(rowIndex).setFieldName(name); // setFieldName handles trimming } catch (DuplicateNameException exc) { throw new InvalidNameException(exc.getMessage()); @@ -1552,9 +1537,6 @@ public abstract class CompEditorModel extends CompositeEditorModel { if (!isContiguousSelection()) { return 0; } - if (isFlexibleArraySelection()) { - return Integer.MAX_VALUE; - } int rowIndex = selection.getFieldRange(0).getStart().getIndex().intValue(); if (rowIndex < getNumComponents()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java index e16c706737..3021dafa68 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java @@ -177,7 +177,13 @@ public class CompEditorPanel extends CompositeEditorPanel { validate(); } - bitViewComponent.init(dtc); + if (dtc != null && dtc.getOffset() >= length) { + // likely trailing zero-length component - not in range for bitViewComponent + bitViewComponent.init(null); + } + else { + bitViewComponent.init(dtc); + } } }); @@ -821,7 +827,7 @@ public class CompEditorPanel extends CompositeEditorPanel { sizeTextField.setEditable(editable); if (editable) { // editable - use same background as category field - sizeTextField.setBackground(categoryStatusTextField.getBackground()); + sizeTextField.setBackground(descriptionTextField.getBackground()); } else { // not editable - use same background as panel diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java index f925f51541..afede9375e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java @@ -177,17 +177,14 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen rowIndex = range.getStart().getIndex().intValue(); } - boolean dynamicSizingAllowed = true; - DataType currentDt = null; DataTypeComponent dtc = getComponent(rowIndex); if (dtc != null) { - dynamicSizingAllowed = !dtc.isFlexibleArrayComponent(); currentDt = dtc.getDataType(); } if (!(currentDt instanceof Pointer)) { // stacking on pointer allows any data type - checkIsAllowableDataType(dt, dynamicSizingAllowed); + checkIsAllowableDataType(dt); } DataType resultDt = DataUtilities.reconcileAppliedDataType(currentDt, dt, true); @@ -358,7 +355,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen dtName = previousDt.getDisplayName(); } DataType newDt = null; - int newLength = 0; + int newLength = -1; if (dataTypeObject instanceof DataTypeInstance) { DataTypeInstance dti = (DataTypeInstance) dataTypeObject; newDt = dti.getDataType(); @@ -381,10 +378,15 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen if (newDt == null) { return; // Was nothing and is nothing. } + + if (DataTypeComponent.usesZeroLengthComponent(newDt)) { + newLength = 0; + } - checkIsAllowableDataType(newDt, element == null || !element.isFlexibleArrayComponent()); + checkIsAllowableDataType(newDt); newDt = resolveDataType(newDt, viewDTM, DataTypeConflictHandler.DEFAULT_HANDLER); + if (newLength < 0) { // prefer previous size first int suggestedLength = (previousLength <= 0) ? lastNumBytes : previousLength; @@ -393,22 +395,22 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen if (sizedDataType == null) { return; } + newDt = resolveDataType(sizedDataType.getDataType(), viewDTM, DataTypeConflictHandler.DEFAULT_HANDLER); newLength = sizedDataType.getLength(); + if (newLength <= 0) { + throw new UsrException("Can't currently add this data type."); + } } if ((previousDt != null) && newDt.isEquivalent(previousDt) && newLength == previousLength) { return; } int maxLength = getMaxReplaceLength(rowIndex); - if (newLength <= 0) { - throw new UsrException("Can't currently add this data type."); - } if (maxLength > 0 && newLength > maxLength) { throw new UsrException(newDt.getDisplayName() + " doesn't fit within " + maxLength + " bytes, need " + newLength + " bytes"); } - setComponentDataTypeInstance(rowIndex, - DataTypeInstance.getDataTypeInstance(newDt, newLength)); + setComponentDataTypeInstance(rowIndex, newDt, newLength); notifyCompositeChanged(); } @@ -458,10 +460,6 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen int max = getMaxElements(); if (isSingleRowSelection()) { if (max != 0) { - int currentIndex = selection.getFieldRange(0).getStart().getIndex().intValue(); - if (canConvertToFlexibleArray(currentIndex)) { - min = 0; // allow flexible array - } int initial = getLastNumElements(); NumberInputDialog numberInputDialog = new NumberInputDialog("elements", ((initial > 0) ? initial : 1), min, max); @@ -492,20 +490,6 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen } } - /** - * Determine if the existing composite component identified by its rowIndex can - * be converted to a flexible array (i.e., unsized array). - * @param rowIndex existing component index - * @return true if conversion to flexible array permitted. - */ - protected boolean canConvertToFlexibleArray(int rowIndex) { - return false; - } - - protected void convertToFlexibleArray(int rowIndex) throws UsrException { - throw new UsrException("Flexible array not permitted"); - } - protected void createArray(int numElements) throws InvalidDataTypeException, DataTypeConflictException, UsrException { if (selection.getNumRanges() != 1) { @@ -777,6 +761,10 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen */ protected boolean nameExistsElsewhere(String name, int rowIndex) { if (name != null) { + name = name.trim(); + if (name.length() == 0) { + return false; + } int numComponents = getNumComponents(); for (int i = 0; i < rowIndex && i < numComponents; i++) { if (name.equals(getComponent(i).getFieldName())) { @@ -797,12 +785,18 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen * If invalid, an exception will be thrown. * * @param datatype the data type - * @param dynamicSizingAllowed true signals to allow dynamic types * @throws InvalidDataTypeException if the structure being edited is part * of the data type being inserted or doesn't have a valid size. */ - protected void checkIsAllowableDataType(DataType datatype, boolean dynamicSizingAllowed) + protected void checkIsAllowableDataType(DataType datatype) throws InvalidDataTypeException { + if (!allowsZeroLengthComponents() && DataTypeComponent.usesZeroLengthComponent(datatype)) { + throw new InvalidDataTypeException( + "Zero-length datatype not permitted: " + datatype.getName()); + } + if (!allowsBitFields() && (datatype instanceof BitFieldDataType)) { + throw new InvalidDataTypeException("Bitfield not permitted: " + datatype.getName()); + } if (datatype instanceof TypeDef) { datatype = ((TypeDef) datatype).getBaseDataType(); } @@ -811,17 +805,21 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen "Factory data types are not " + "allowed in a composite data type."); } else if (datatype instanceof Dynamic) { - if (!dynamicSizingAllowed) { - throw new InvalidDataTypeException( - "Dynamic data type is not permitted in current context"); - } - else if (!((Dynamic) datatype).canSpecifyLength()) { + if (!((Dynamic) datatype).canSpecifyLength()) { throw new InvalidDataTypeException("Non-sizable Dynamic data types are not " + "allowed in a composite data type."); } } } + protected boolean allowsZeroLengthComponents() { + return true; + } + + protected boolean allowsBitFields() { + return true; + } + @Override public boolean isAddAllowed(int currentIndex, DataType datatype) { return false; @@ -1114,7 +1112,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen throw new AssertException("Can't set data type to null."); } - checkIsAllowableDataType(newDt, element == null || !element.isFlexibleArrayComponent()); + checkIsAllowableDataType(newDt); newLength = newDt.getLength(); if (newLength < 0) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java index b9057ec667..a7ae414a26 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java @@ -32,7 +32,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager { * @param composite the composite data type that is being edited. (cannot be null). */ public CompositeViewerDataTypeManager(String rootName, Composite composite) { - super(rootName); + super(rootName, composite.getDataTypeManager().getDataOrganization()); transactionID = startTransaction(""); originalDTM = composite.getDataTypeManager(); universalID = originalDTM.getUniversalID(); // mimic original DTM @@ -50,10 +50,4 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager { return originalDTM.getType(); } - @Override - public DataOrganization getDataOrganization() { - // get the alignment from the original data type manager. - return originalDTM.getDataOrganization(); - } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java index 8e966a9f24..5a6b46f121 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java @@ -516,10 +516,7 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager * Returns the number of component rows in the viewer. There may be a * blank row at the end for selecting. Therefore this number can be * different than the actual number of components currently in the - * structure being viewed. In addition, if a flexible array component - * exists which is not included in {@link #getNumComponents()} it - * will be included in this count and would appear after the blank row - * (e.g., getComponent(getNumComponents()+1) ). + * structure being viewed. * * @return the number of rows in the model */ @@ -530,23 +527,17 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager /** * Returns the number of components in this structure or union. - * NOTE: This number does not include the flexible array component which may exist - * within a structure. * @return the number of components in the model */ public int getNumComponents() { - if (viewComposite == null) { - return 0; - } - return viewComposite.getNumComponents(); + return (viewComposite != null) ? viewComposite.getNumComponents() : 0; } /** * Return the nth component for the structure being viewed. Since the number of rows * can exceed the number of components defined within the composite * ({@link Composite#getNumComponents()}) this method will return null for a blank - * row or a flexible array component which may appear as an additional rows for - * structures. + * row. * @param rowIndex the index of the component to return. First component is index of 0. * @return the component */ @@ -588,37 +579,34 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager return ""; } String value; - DataType dt; - int dtLen; + DataTypeComponent dtc = viewComposite.getComponent(rowIndex); if (columnIndex == getOffsetColumn()) { - int offset = viewComposite.getComponent(rowIndex).getOffset(); + int offset = dtc.getOffset(); value = showHexNumbers ? getHexString(offset, true) : Integer.toString(offset); } else if (columnIndex == getLengthColumn()) { - int length = viewComposite.getComponent(rowIndex).getLength(); - value = showHexNumbers ? getHexString(length, true) : Integer.toString(length); + int compLen = dtc.getLength(); + value = showHexNumbers ? getHexString(compLen, true) : Integer.toString(compLen); } else if (columnIndex == getMnemonicColumn()) { - DataTypeComponent comp = viewComposite.getComponent(rowIndex); - dt = comp.getDataType(); + DataType dt = dtc.getDataType(); value = dt.getMnemonic(new SettingsImpl()); - int compLen = comp.getLength(); - dtLen = comp.getDataType().getLength(); + int compLen = dtc.getLength(); + int dtLen = dt.isZeroLength() ? 0 : dt.getLength(); if (dtLen > compLen) { value = "TooBig: " + value + " needs " + dtLen + " has " + compLen; } } else if (columnIndex == getDataTypeColumn()) { - DataTypeComponent dtc = viewComposite.getComponent(rowIndex); - dt = dtc.getDataType(); - dtLen = dt.getLength(); + DataType dt = dtc.getDataType(); + int dtLen = dt.getLength(); return DataTypeInstance.getDataTypeInstance(dt, (dtLen > 0) ? dtLen : dtc.getLength()); } else if (columnIndex == getNameColumn()) { - value = viewComposite.getComponent(rowIndex).getFieldName(); + value = dtc.getFieldName(); } else if (columnIndex == getCommentColumn()) { - value = viewComposite.getComponent(rowIndex).getComment(); + value = dtc.getComment(); } else { value = "UNKNOWN"; @@ -1173,20 +1161,6 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager return (selection.getNumRanges() == 1); } - /** - * Returns true if the current single row selection corresponds to a flexible array component - * @return true if the current single row selection corresponds to a flexible array component - */ - public boolean isFlexibleArraySelection() { - if (!isSingleRowSelection()) { - return false; - } - FieldRange range = selection.getFieldRange(0); - int rowIndex = range.getStart().getIndex().intValue(); - DataTypeComponent component = getComponent(rowIndex); - return component != null && component.isFlexibleArrayComponent(); - } - /** * Returns true if the component list selection is a single component * @return true if the component list selection is a single component diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java index 8e9c8d4e7b..3cad1320c8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DataTypeHelper.java @@ -102,9 +102,7 @@ public class DataTypeHelper { if (newDt == null) { throw new InvalidDataTypeException("valid data-type not specified"); } - DataTypeComponent element = editModel.getComponent(index); - editModel.checkIsAllowableDataType(newDt, - element == null || !element.isFlexibleArrayComponent()); + editModel.checkIsAllowableDataType(newDt); int mrl = editModel.getMaxReplaceLength(index); if ((mrl != -1) && (newDt.getLength() > mrl)) { throw new InvalidDataTypeException(newDt.getDisplayName() + " doesn't fit within " + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java index e915024ec2..3c0c154eb3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java @@ -214,9 +214,11 @@ public interface EditorModel { /** * Sets the data type for the component at the indicated row index. * @param rowIndex the row index of the component - * @param dataTypeInstance + * @param dt component datatype + * @param length component length + * @throws UsrException if invalid datatype or length specified */ - public void setComponentDataTypeInstance(int rowIndex, DataTypeInstance dataTypeInstance) + public void setComponentDataTypeInstance(int rowIndex, DataType dt, int length) throws UsrException; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java index f731f37cc0..6244ebd3a8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java @@ -102,10 +102,6 @@ class StructureEditorModel extends CompEditorModel { public int getRowCount() { int componentCount = getNumComponents(); int rowCount = componentCount + 1; // add blank edit row - Structure viewStruct = (Structure) viewComposite; - if (viewStruct != null && viewStruct.hasFlexibleArrayComponent()) { - ++rowCount; - } return rowCount; } @@ -129,7 +125,6 @@ class StructureEditorModel extends CompEditorModel { } DataTypeComponent dtc = getComponent(rowIndex); - if (dtc == null) { if (columnIndex == getDataTypeColumn()) { return null; @@ -137,42 +132,34 @@ class StructureEditorModel extends CompEditorModel { return ""; } - boolean isFlexArrayComponent = dtc.isFlexibleArrayComponent(); String value = null; - DataType dt; - int dtLen; if (columnIndex == getOffsetColumn()) { int offset = dtc.getOffset(); value = showHexNumbers ? getHexString(offset, true) : Integer.toString(offset); } else if (columnIndex == getLengthColumn()) { - int length = dtc.getLength(); - value = showHexNumbers ? getHexString(length, true) : Integer.toString(length); + int compLen = dtc.getLength(); + value = showHexNumbers ? getHexString(compLen, true) : Integer.toString(compLen); } else if (columnIndex == getMnemonicColumn()) { - dt = dtc.getDataType(); + DataType dt = dtc.getDataType(); value = dt.getMnemonic(new SettingsImpl()); - if (isFlexArrayComponent) { - value += "[0]"; - } - else { - int compLen = dtc.getLength(); - dtLen = dtc.getDataType().getLength(); - if (dtLen > compLen) { - value = "TooBig: " + value + " needs " + dtLen + " has " + compLen; - } + int compLen = dtc.getLength(); + int dtLen = dt.isZeroLength() ? 0 : dt.getLength(); + if (dtLen > compLen) { + value = "TooBig: " + value + " needs " + dtLen + " has " + compLen; } } else if (columnIndex == getDataTypeColumn()) { - dt = dtc.getDataType(); - dtLen = dt.getLength(); + DataType dt = dtc.getDataType(); + int dtLen = dt.getLength(); return DataTypeInstance.getDataTypeInstance(dt, (dtLen > 0) ? dtLen : dtc.getLength()); } else if (columnIndex == getNameColumn()) { - value = getComponent(rowIndex).getFieldName(); + value = dtc.getFieldName(); } else if (columnIndex == getCommentColumn()) { - value = getComponent(rowIndex).getComment(); + value = dtc.getComment(); } return (value == null) ? "" : value; @@ -185,9 +172,6 @@ class StructureEditorModel extends CompEditorModel { return null; } Structure viewStruct = (Structure) viewComposite; - if (rowIndex == (numComponents + 1)) { - return viewStruct.getFlexibleArrayComponent(); - } if (rowIndex > numComponents) { return null; } @@ -203,73 +187,48 @@ class StructureEditorModel extends CompEditorModel { return viewComposite == null ? 0 : viewComposite.getNumComponents(); } - @Override - protected boolean canConvertToFlexibleArray(int rowIndex) { - if (!(viewComposite instanceof Structure)) { - return false; - } - if (((Structure) viewComposite).hasFlexibleArrayComponent()) { - return false; - } - if (rowIndex != (getNumComponents() - 1)) { - return false; - } - DataTypeComponent dtc = getComponent(rowIndex); - DataType dt = dtc.getDataType(); - if (dt instanceof TypeDef) { - dt = ((TypeDef) dt).getBaseDataType(); - } - if (dt instanceof Dynamic || dt instanceof FactoryDataType) { - return false; - } - return true; - } - - @Override - protected void convertToFlexibleArray(int rowIndex) throws UsrException { - if (!canConvertToFlexibleArray(rowIndex)) { - // should be avoided by constraining minimum array size and data type - throw new UsrException("Flexible array not permitted"); - } - DataTypeComponent dtc = getComponent(rowIndex); - Structure struct = (Structure) viewComposite; - struct.setFlexibleArrayComponent(dtc.getDataType(), dtc.getFieldName(), dtc.getComment()); - delete(rowIndex); - selection.addRange(rowIndex + 1, rowIndex + 2); // select flex component - selectionChanged(); - } - @Override protected boolean isSizeEditable() { return !isPackingEnabled(); } void setStructureSize(int size) { - if (viewComposite == null) { + if (viewComposite == null || viewComposite.isPackingEnabled()) { return; } - int currentLength = viewComposite.isZeroLength() ? 0 : viewComposite.getLength(); + int currentLength = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength(); if (currentLength == size) { return; } Structure structure = (Structure) viewComposite; + if (currentLength > size) { + int numComponents = structure.getNumComponents(); + + DataTypeComponent dtc = structure.getComponentContaining(size); + int ordinal = dtc.getOrdinal(); + + // retain any zero-length components which have an offset equal the new size + while (dtc.getOffset() == size && dtc.getLength() == 0 && + (ordinal + 1) < numComponents) { + dtc = structure.getComponent(++ordinal); + } + + // remove trailing components outside of new size + for (int index = numComponents - 1; index >= ordinal; index--) { + structure.delete(index); + int bitFieldResidualBytes = structure.getNumComponents() - index; + for (int i = 0; i < bitFieldResidualBytes; i++) { + // bitfield removal may cause injection of undefined bytes - remove them + structure.delete(index); + } + } + // structure may shrink too much from component removal - may need to grow + currentLength = (viewComposite.isZeroLength()) ? 0 : viewComposite.getLength(); + } if (currentLength < size) { // Increasing structure length. structure.growStructure(size - currentLength); } - else { - DataTypeComponent dtc = structure.getComponentAt(size); - int ordinal = dtc.getOrdinal(); - if (dtc.getOffset() != size) { - structure.clearComponent(ordinal); - dtc = structure.getComponentAt(size); - ordinal = dtc.getOrdinal(); - } - int numComponents = structure.getNumComponents(); - for (int index = numComponents - 1; index >= ordinal; index--) { - structure.delete(index); - } - } updateAndCheckChangeState(); fireTableDataChanged(); } @@ -292,9 +251,6 @@ class StructureEditorModel extends CompEditorModel { if (dtc == null) { return false; } - if (dtc.isFlexibleArrayComponent()) { - return true; - } DataType dt = dtc.getDataType(); if (dt == DataType.DEFAULT) { return false; @@ -398,12 +354,12 @@ class StructureEditorModel extends CompEditorModel { } DataTypeComponent originalComp = getComponent(index); - if (originalComp == null || originalComp.isFlexibleArrayComponent()) { + if (originalComp == null) { throw new IllegalArgumentException("Invalid component index specified"); } DataType dt = originalComp.getDataType(); int dtLen = dt.getLength(); - checkIsAllowableDataType(dt, true); + checkIsAllowableDataType(dt); int startIndex = index + 1; if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) { @@ -570,7 +526,7 @@ class StructureEditorModel extends CompEditorModel { @Override public boolean isBitFieldAllowed() { - return isSingleRowSelection() && !isFlexibleArraySelection(); + return isSingleRowSelection(); } /** @@ -587,7 +543,7 @@ class StructureEditorModel extends CompEditorModel { FieldRange range = selection.getFieldRange(0); DataTypeComponent comp = getComponent(range.getStart().getIndex().intValue()); - if (comp == null || comp.isFlexibleArrayComponent() || comp.isBitFieldComponent()) { + if (comp == null || comp.isBitFieldComponent()) { return false; } @@ -615,29 +571,6 @@ class StructureEditorModel extends CompEditorModel { return getComponent(rowIndex) != null; } - @Override - public void deleteSelectedComponents() throws UsrException { - if (!isDeleteAllowed()) { - throw new UsrException("Deleting is not allowed."); - } - if (isEditingField()) { - endFieldEditing(); - } - - int rowIndex = selection.getFieldRange(0).getStart().getIndex().intValue(); - DataTypeComponent dtc = getComponent(rowIndex); - if (dtc.isFlexibleArrayComponent()) { - // Remove flexible array component - ((Structure) viewComposite).clearFlexibleArrayComponent(); - componentEdited(); - selection.addRange(rowIndex - 1, rowIndex); - fixSelection(); - selectionChanged(); - return; - } - super.deleteSelectedComponents(); - } - @Override public boolean isDuplicateAllowed() { @@ -813,7 +746,7 @@ class StructureEditorModel extends CompEditorModel { } try { - checkIsAllowableDataType(dataType, !dtc.isFlexibleArrayComponent()); + checkIsAllowableDataType(dataType); } catch (InvalidDataTypeException e) { return false; @@ -898,9 +831,7 @@ class StructureEditorModel extends CompEditorModel { else if (comp == null) { return 0; // No such component. Not at valid edit index. } - else if (comp.isFlexibleArrayComponent()) { - return Integer.MAX_VALUE; - } + // Otherwise, get size of component and number of Undefined bytes after it. FieldRange range = getSelectedRangeContaining(currentIndex); if (range == null || @@ -936,28 +867,20 @@ class StructureEditorModel extends CompEditorModel { @Override protected DataTypeComponent insert(int rowIndex, DataType dataType, int length, String name, String comment) throws InvalidDataTypeException { - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); try { - DataTypeComponent dtc = getComponent(rowIndex); - if (dtc != null && dtc.isFlexibleArrayComponent()) { - Structure struct = (Structure) viewComposite; - dtc = struct.setFlexibleArrayComponent(dataType, dtc.getFieldName(), - dtc.getComment()); + DataTypeComponent dtc; + if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) { + dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name, comment); } else { - if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) { - dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name, - comment); - } - else { - BitFieldDataType bitfield = (BitFieldDataType) dataType; - dtc = ((Structure) viewComposite).insertBitField(rowIndex, length, - bitfield.getBitOffset(), bitfield.getBaseDataType(), - bitfield.getDeclaredBitSize(), name, comment); - } - if (rowIndex <= row) { - row++; - } + BitFieldDataType bitfield = (BitFieldDataType) dataType; + dtc = ((Structure) viewComposite).insertBitField(rowIndex, length, + bitfield.getBitOffset(), bitfield.getBaseDataType(), + bitfield.getDeclaredBitSize(), name, comment); + } + if (rowIndex <= row) { + row++; } adjustSelection(rowIndex, 1); // Consume undefined bytes that may have been added, if needed. @@ -973,7 +896,7 @@ class StructureEditorModel extends CompEditorModel { protected void insert(int rowIndex, DataType dataType, int length, int numCopies, TaskMonitor monitor) throws InvalidDataTypeException, CancelledException { - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); int componentOrdinal = convertRowToOrdinal(rowIndex); monitor.initialize(numCopies); try { @@ -997,21 +920,10 @@ class StructureEditorModel extends CompEditorModel { } } - @Override - public DataTypeComponent replace(int rowIndex, DataType dt) throws UsrException { - DataTypeComponent dtc = getComponent(rowIndex); - if (dtc == null || !dtc.isFlexibleArrayComponent()) { - return super.replace(rowIndex, dt); - } - Structure struct = (Structure) viewComposite; - return struct.setFlexibleArrayComponent(dt, dtc.getFieldName(), dtc.getComment()); - } - @Override protected DataTypeComponent replace(int rowIndex, DataType dataType, int length, String name, String comment) throws InvalidDataTypeException { - // It is assumed that the replaced component is not a flexible array - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); try { DataTypeComponent dtc = null; boolean isSelected = selection.containsEntirely(BigInteger.valueOf(rowIndex)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java index 3e5a8420e1..bab34d67dd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java @@ -294,7 +294,7 @@ class UnionEditorModel extends CompEditorModel { if (currentIndex < 0 || currentIndex > getNumComponents()) { return false; } - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); } catch (InvalidDataTypeException e) { return false; @@ -378,7 +378,7 @@ class UnionEditorModel extends CompEditorModel { @Override public DataTypeComponent insert(int rowIndex, DataType dataType, int length, String name, String comment) throws InvalidDataTypeException { - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); try { DataTypeComponent dtc = ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment); @@ -409,7 +409,7 @@ class UnionEditorModel extends CompEditorModel { @Override public DataTypeComponent replace(int rowIndex, DataType dataType, int length, String name, String comment) throws InvalidDataTypeException { - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); try { boolean isSelected = selection.containsEntirely(BigInteger.valueOf(rowIndex)); ((Union) viewComposite).delete(rowIndex); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java index a9f2a5df0f..e38fc48f40 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java @@ -63,6 +63,8 @@ import util.CollectionUtils; //@formatter:on public class DataTypePreviewPlugin extends ProgramPlugin { + private static final String ROOT_NAME = "DataTypePreviewer"; + private DTPPComponentProvider provider; private DTPPTableModel model; private DTPPTable table; @@ -71,7 +73,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { private GoToService goToService; private DockingAction addAction; private DockingAction deleteAction; - private LayeredDataTypeManager dataTypeManager; + private DataTypeManager dataTypeManager; private Program activeProgram; private SwingUpdateManager updateManager = new SwingUpdateManager(650, () -> updatePreview()); @@ -101,7 +103,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { model = new DTPPTableModel(); table = new DTPPTable(model); component = new DTPPScrollPane(table); - dataTypeManager = new LayeredDataTypeManager(activeProgram); + dataTypeManager = createLayeredDataTypeManager(); addDataType(new ByteDataType()); addDataType(new WordDataType()); @@ -177,7 +179,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { private void updateModel() { - LayeredDataTypeManager newDtm = new LayeredDataTypeManager(activeProgram); + DataTypeManager newDtm = createLayeredDataTypeManager(); int transactionId = newDtm.startTransaction("add datatypes"); try { @@ -197,7 +199,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { List dtPaths = getModelDataTypePaths(); model.removeAll(); - LayeredDataTypeManager oldDtm = dataTypeManager; + DataTypeManager oldDtm = dataTypeManager; dataTypeManager = newDtm; oldDtm.close(); @@ -711,24 +713,11 @@ public class DataTypePreviewPlugin extends ProgramPlugin { } } - private class LayeredDataTypeManager extends StandAloneDataTypeManager { - - DataOrganization layeredDataOrganization1; - - public LayeredDataTypeManager(Program program) { - super("DataTypePreviewer"); - this.layeredDataOrganization1 = - program != null ? program.getDataTypeManager().getDataOrganization() : null; - } - - @Override - public DataOrganization getDataOrganization() { - if (layeredDataOrganization1 == null) { - return super.getDataOrganization(); - } - return layeredDataOrganization1; - } - + private DataTypeManager createLayeredDataTypeManager() { + DataOrganization dataOrg = + (activeProgram != null) ? activeProgram.getCompilerSpec().getDataOrganization() + : DataOrganizationImpl.getDefaultOrganization(); + return new StandAloneDataTypeManager(ROOT_NAME, dataOrg); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/DataTypeManagerStub.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/DataTypeManagerStub.java deleted file mode 100644 index 35c68fd9b2..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/DataTypeManagerStub.java +++ /dev/null @@ -1,62 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.function.editor; - -import ghidra.program.model.data.*; - -class DataTypeManagerStub extends StandAloneDataTypeManager { - - protected DataTypeManagerStub(String name) { - super(name); - populate(); - } - - /** - * Add the built in data types to the default built in folder if they - * were not found in any other category. - */ - protected void populate() { - int id = super.startTransaction("Populate"); - try { - resolve(new ByteDataType(), null); - resolve(new CharDataType(), null); - resolve(new BooleanDataType(), null); - resolve(new DoubleDataType(), null); - resolve(new StringDataType(), null); - resolve(new Undefined1DataType(), null); - resolve(new Undefined2DataType(), null); - resolve(new Undefined4DataType(), null); - resolve(new Undefined8DataType(), null); - resolve(new UnicodeDataType(), null); - resolve(new VoidDataType(), null); - resolve(new IntegerDataType(), null); - resolve(new ShortDataType(), null); - - StructureDataType struct1 = new StructureDataType("abc", 4); - struct1.setCategoryPath(new CategoryPath("/foo")); - resolve(struct1, null); - - StructureDataType struct2 = new StructureDataType("abc", 4); - struct2.setCategoryPath(new CategoryPath("/bar")); - resolve(struct2, null); - } - finally { - super.endTransaction(id, true); - } - - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java index 54e5b525c3..a3f4af40de 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java @@ -55,6 +55,18 @@ public abstract class BiDirectionDataType extends StructureDataType this.splitOffset = splitOffset; } + @Override + protected DataType validateDataType(DataType dataType) { + if (DataTypeComponent.usesZeroLengthComponent(dataType)) { + throw new IllegalArgumentException( + "Zero-length datatype not permitted: " + dataType.getName()); + } + if (dataType instanceof BitFieldDataType) { + throw new IllegalArgumentException("Bitfield not permitted: " + dataType.getName()); + } + return super.validateDataType(dataType); + } + @Override public int getAlignment() { throw new UnsupportedOperationException( @@ -91,18 +103,11 @@ public abstract class BiDirectionDataType extends StructureDataType // ignore } - @Override - public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, - String comment) { - throw new UnsupportedOperationException( - "BiDirectionDataType.setFlexibleArrayComponent() not implemented."); - } - protected DataTypeComponent getDefinedComponentAt(int offset) { if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) { return null; } - int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); if (index >= 0) { return components.get(index); } @@ -114,7 +119,7 @@ public abstract class BiDirectionDataType extends StructureDataType if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) { return null; } - int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); if (index >= 0) { return components.get(index); } @@ -240,7 +245,7 @@ public abstract class BiDirectionDataType extends StructureDataType if (ordinal < 0 || ordinal >= numComponents) { throw new IndexOutOfBoundsException(ordinal); } - int idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); + int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); if (idx >= 0) { return components.get(idx); } @@ -248,11 +253,11 @@ public abstract class BiDirectionDataType extends StructureDataType } @Override - public DataTypeComponent getComponent(int ordinal) { + public DataTypeComponentImpl getComponent(int ordinal) { if (ordinal < 0 || ordinal >= numComponents) { throw new IndexOutOfBoundsException(ordinal); } - int idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); + int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); if (idx >= 0) { return components.get(idx); } @@ -307,7 +312,7 @@ public abstract class BiDirectionDataType extends StructureDataType checkAncestry(dataType); dataType = dataType.clone(getDataTypeManager()); - int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); int additionalShift = 0; if (index >= 0) { @@ -443,7 +448,7 @@ public abstract class BiDirectionDataType extends StructureDataType " within a defined component in " + getDisplayName() + "."); } else { - definedIndex = Collections.binarySearch(components, new Integer(dtc.getOrdinal()), + definedIndex = Collections.binarySearch(components, Integer.valueOf(dtc.getOrdinal()), ordinalComparator); if (definedIndex < 0) { definedIndex = -definedIndex - 1; @@ -470,7 +475,7 @@ public abstract class BiDirectionDataType extends StructureDataType throw new IllegalArgumentException( "Offset " + offset + " is not in " + getDisplayName() + "."); } - int index = Collections.binarySearch(components, new Integer(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); int length = 1; if (index < 0) { @@ -485,6 +490,25 @@ public abstract class BiDirectionDataType extends StructureDataType numComponents--; } + @Override + public void clearAtOffset(int offset) { + if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) { + throw new IllegalArgumentException( + "Offset " + offset + " is not in " + getDisplayName() + "."); + } + int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + if (index >= 0) { + DataTypeComponent dtc = components.remove(index); + dtc.getDataType().removeParent(this); + int len = dtc.getLength(); + if (len > 1) { + int deltaLength = len - 1; + shiftOffsets(index, deltaLength, 0); + numComponents += deltaLength; + } + } + } + @Override public boolean isEquivalent(DataType dataType) { if (dataType == this) { @@ -531,19 +555,19 @@ public abstract class BiDirectionDataType extends StructureDataType public abstract BiDirectionDataType clone(DataTypeManager dtm); @Override - public void clearComponent(int index) { - if (index < 0 || index >= numComponents) { - throw new IndexOutOfBoundsException(index); + public void clearComponent(int ordinal) { + if (ordinal < 0 || ordinal >= numComponents) { + throw new IndexOutOfBoundsException(ordinal); } - int idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); - if (idx >= 0) { - DataTypeComponent dtc = components.remove(idx); + int index = + Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); + if (index >= 0) { + DataTypeComponent dtc = components.remove(index); dtc.getDataType().removeParent(this); int len = dtc.getLength(); - int deltaLength = len - 1; -// int offset = dtc.getOffset(); if (len > 1) { - shiftOffsets(idx, deltaLength, 0); + int deltaLength = len - 1; + shiftOffsets(index, deltaLength, 0); numComponents += deltaLength; } } @@ -644,7 +668,8 @@ public abstract class BiDirectionDataType extends StructureDataType DataTypeComponentImpl newDtc = new DataTypeComponentImpl(dataType, this, length, ordinal, newOffset, newName, comment); dataType.addParent(this); - int index = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); + int index = + Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); if (index < 0) { index = -index - 1; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java index ed3a55eefb..ab14a9e4ca 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java @@ -79,6 +79,16 @@ class StackEditorModel extends CompositeEditorModel { } } + @Override + protected boolean allowsZeroLengthComponents() { + return false; + } + + @Override + protected boolean allowsBitFields() { + return false; + } + void stackChangedExcternally(boolean changed) { stackChangedExternally = changed; } @@ -612,7 +622,7 @@ class StackEditorModel extends CompositeEditorModel { if (currentIndex < 0 || currentIndex >= getRowCount()) { return false; } - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); } catch (InvalidDataTypeException e) { return false; @@ -720,7 +730,7 @@ class StackEditorModel extends CompositeEditorModel { if (currentIndex < 0 || currentIndex >= getRowCount()) { return false; } - checkIsAllowableDataType(dataType, true); + checkIsAllowableDataType(dataType); } catch (InvalidDataTypeException e) { return false; @@ -788,10 +798,9 @@ class StackEditorModel extends CompositeEditorModel { } @Override - public void setComponentDataTypeInstance(int index, DataTypeInstance dti) throws UsrException { - DataType dt = dti.getDataType(); - checkIsAllowableDataType(dt, true); - ((StackFrameDataType) viewComposite).setDataType(index, dt, dti.getLength()); + public void setComponentDataTypeInstance(int index, DataType dt, int length) throws UsrException { + checkIsAllowableDataType(dt); + ((StackFrameDataType) viewComposite).setDataType(index, dt, length); } @Override @@ -806,7 +815,7 @@ class StackEditorModel extends CompositeEditorModel { public void setComponentName(int rowIndex, String newName) throws InvalidInputException, InvalidNameException, DuplicateNameException { - if (newName.equals("")) { + if (newName.trim().length() == 0) { newName = null; } // if (nameExistsElsewhere(newName, currentIndex)) { @@ -1330,7 +1339,7 @@ class StackEditorModel extends CompositeEditorModel { int newLength = newDt.getLength(); - checkIsAllowableDataType(newDt, true); + checkIsAllowableDataType(newDt); newDt = DataTypeHelper.resolveDataType(newDt, viewDTM, null); int maxLength = getMaxReplaceLength(index); if (newLength <= 0) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java index 3b9eec09e1..2761def8c1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java @@ -25,6 +25,7 @@ import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.SymbolUtilities; import ghidra.util.InvalidNameException; +import ghidra.util.SystemUtilities; import ghidra.util.exception.AssertException; import ghidra.util.exception.InvalidInputException; @@ -410,22 +411,22 @@ public class StackFrameDataType extends BiDirectionDataType { * * @param ordinal the ordinal * @param name the new name. Null indicates the default name. + * @return true if name change was successful, else false */ public boolean setName(int ordinal, String name) { validateName(ordinal, name); DataTypeComponent comp = getComponent(ordinal); String fieldName = comp.getFieldName(); - if ((name != null) && ((name.length() == 0) || (isDefaultName(name)))) { - name = null; - } - if (name == null) { - if (fieldName == null) { - return false; + if (name != null) { + name = name.trim(); + if (name.length() == 0 || isDefaultName(name)) { + name = null; } } - else if (name.equals(fieldName)) { + if (SystemUtilities.isEqual(name, fieldName)) { return false; } + DataType dt = comp.getDataType(); int length = comp.getLength(); String comment = comp.getComment(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java index e26702c39c..f6a4228f34 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java @@ -163,10 +163,16 @@ class DWARFDataTypeConflictHandler extends DataTypeConflictHandler { // isEquiv(). // Ensure that two components in the partial struct don't map to the same // component in the full structure. + for (DataTypeComponent partDTC : partComps) { + DataType partDT = partDTC.getDataType(); + if (partDT.isZeroLength()) { + // don't try to match zero length fields, so skip + continue; + } DataTypeComponent fullDTCAt = (partDTC.getDataType() instanceof BitFieldDataType) ? getBitfieldByOffsets(full, partDTC) - : full.getComponentAt(partDTC.getOffset()); + : getBestMatchingDTC(full, partDTC); if (fullDTCAt == null || fullDTCAt.getOffset() != partDTC.getOffset() || !SystemUtilities.isEqual(fullDTCAt.getFieldName(), partDTC.getFieldName())) { return false; @@ -175,15 +181,20 @@ class DWARFDataTypeConflictHandler extends DataTypeConflictHandler { return false; } } - if ( part.getFlexibleArrayComponent() != null ) { - return full.getFlexibleArrayComponent() != null && - isMemberFieldPartiallyCompatible(full.getFlexibleArrayComponent(), - part.getFlexibleArrayComponent(), visitedDataTypes); - } return true; } + DataTypeComponent getBestMatchingDTC(Structure struct, DataTypeComponent matchCriteria) { + for (DataTypeComponent dtc : struct.getComponentsContaining(matchCriteria.getOffset())) { + DataType dt = dtc.getDataType(); + if (dtc.getOffset() == matchCriteria.getOffset() && !dt.isZeroLength()) { + return dtc; + } + } + return null; + } + boolean isMemberFieldPartiallyCompatible(DataTypeComponent fullDTC, DataTypeComponent partDTC, Set visitedDataTypes) { DataType partDT = partDTC.getDataType(); @@ -216,7 +227,7 @@ class DWARFDataTypeConflictHandler extends DataTypeConflictHandler { private DataTypeComponent getBitfieldByOffsets(Structure full, DataTypeComponent partDTC) { BitFieldDataType partBF = (BitFieldDataType) partDTC.getDataType(); - DataTypeComponent fullDTC = full.getComponentAt(partDTC.getOffset()); + DataTypeComponent fullDTC = full.getComponentContaining(partDTC.getOffset()); if (fullDTC == null) { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java index e8fd661d1f..4a8b6e2346 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java @@ -223,11 +223,6 @@ public class DWARFDataTypeImporter { /** * Returns true if the previously imported data type should be reused. *

- * Don't re-use previously imported single-element - * Ghidra array datatypes because they may have actually been an empty array - * definition and we need the special meta-data flag DWARFDataType.isEmptyArrayType - * which is only available in a freshly created DWARFDataType. - *

* Don't re-use empty structs (isNotYetDefined) to ensure that newer * definitions of the same struct are given a chance to be resolved() * into the DTM. @@ -236,10 +231,7 @@ public class DWARFDataTypeImporter { * @return boolean true if its okay to reuse the data type */ private boolean shouldReuseAlreadyImportedDT(DataType alreadyImportedDT) { - return alreadyImportedDT != null && - !alreadyImportedDT.isNotYetDefined() && - !(alreadyImportedDT instanceof Array && - ((Array) alreadyImportedDT).getNumElements() == 1); + return alreadyImportedDT != null && !alreadyImportedDT.isNotYetDefined(); } /* @@ -552,12 +544,19 @@ public class DWARFDataTypeImporter { structSize = 0; } boolean isUnion = diea.getTag() == DWARFTag.DW_TAG_union_type; + boolean isDecl = diea.getBool(DWARFAttribute.DW_AT_declaration, false); DataType struct = isUnion ? new UnionDataType(dni.getParentCP(), dni.getName(), dataTypeManager) : new StructureDataType(dni.getParentCP(), dni.getName(), (int) structSize, dataTypeManager); + if (!isDecl && origStructSize == 0) { + // Enable packing on 0-byte composites so they are treated as defined + // and will not take space if used in a field in another composite. + ((Composite) struct).setToDefaultPacking(); + } + DWARFDataType result = new DWARFDataType(struct, dni, diea.getOffset()); result.dsi = DWARFSourceInfo.create(diea); @@ -719,6 +718,9 @@ public class DWARFDataTypeImporter { } if (union.getLength() < unionSize) { + // NOTE: this is likely due incorrect alignment for union or one or more of its components. + // Default alignment is 1 for non-packed unions and structures. + // if the Ghidra union data type is smaller than the DWARF union, pad it out DataType padding = Undefined.getUndefinedDataType((int) unionSize); try { @@ -773,11 +775,14 @@ public class DWARFDataTypeImporter { DataTypeComponent[] definedComponents = structure.getDefinedComponents(); for (int i = 0; i < definedComponents.length; i++) { DataTypeComponent dtc = definedComponents[i]; + DataType dtcDT = dtc.getDataType(); + if (dtcDT.isZeroLength()) { + continue; + } int nextDTCOffset = (i < definedComponents.length - 1) ? definedComponents[i + 1].getOffset() : structure.getLength(); - int emptySpaceBetween = nextDTCOffset - dtc.getEndOffset(); - DataType dtcDT = dtc.getDataType(); + int emptySpaceBetween = nextDTCOffset - (dtc.getEndOffset() + 1); if (dtc.getLength() < dtcDT.getLength() && emptySpaceBetween > 0) { DataTypeComponent newDTC = structure.replaceAtOffset(dtc.getOffset(), dtcDT, Math.min(nextDTCOffset - dtc.getOffset(), dtc.getDataType().getLength()), @@ -802,17 +807,15 @@ public class DWARFDataTypeImporter { DataTypeComponent[] definedComponents = structure.getDefinedComponents(); if (definedComponents.length > 0) { DataTypeComponent lastDTC = definedComponents[definedComponents.length - 1]; - return lastDTC.getOffset() + getUnpaddedDataTypeLength(lastDTC.getDataType()); + return lastDTC.getOffset() + lastDTC.getLength(); } } - return dt.getLength(); + return dt.isZeroLength() ? 0 : dt.getLength(); } private void populateStubStruct_worker(DWARFDataType ddt, StructureDataType structure, DIEAggregate diea, int childTagType) throws IOException, DWARFExpressionException { - Set conflictingZeroLenFields = getConflictingZeroLenFields(diea, childTagType); - for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren(childTagType)) { DIEAggregate childDIEA = prog.getAggregate(childEntry); @@ -870,19 +873,6 @@ public class DWARFDataTypeImporter { } } - if (conflictingZeroLenFields.contains(childEntry.getOffset())) { - // Skip adding this member because it is a problematic zero-length - // field - DWARFUtil.appendDescription(structure, - memberDesc("Missing member", "zero-length member", memberName, childDT, - memberOffset, -1, -1), - "\n"); - continue; - } - - boolean isDynamicSizedType = (childDT.dataType instanceof Dynamic || - childDT.dataType instanceof FactoryDataType); - //if (childDT.getPathName().equals(structure.getPathName()) && childDT != structure) { // The child we are adding has the exact same fullpath as us. // This can happen when DWARF namespace info gets squished and two types @@ -892,33 +882,7 @@ public class DWARFDataTypeImporter { // TODO: rename parent struct here. use .conflict or _basetype? //} - if (childDT.isEmptyArrayType && childDT.dataType instanceof Array) { - - if (memberOffset == structure.getLength() && - structure.getFlexibleArrayComponent() == null) { - DataType arrayElementType = ((Array) childDT.dataType).getDataType(); - structure.setFlexibleArrayComponent(arrayElementType, memberName, null); - } - else { - DWARFUtil.appendDescription(structure, - memberDesc("Missing member", - "Unsupported interior flex array: " + childDT.dataType.getName(), - memberName, childDT, memberOffset, -1, -1), - "\n"); - - } - - // skip the rest of this loop as it deals with adding component children members. - continue; - } - if (isBitField) { - if (isDynamicSizedType) { - DWARFUtil.appendDescription(structure, memberDesc("Missing member", - "dynamic length type", memberName, childDT, memberOffset, bitSize, -1), - "\n"); - continue; - } if (!BitFieldDataType.isValidBaseDataType(childDT.dataType)) { DWARFUtil.appendDescription(structure, memberDesc("Missing member", @@ -982,16 +946,14 @@ public class DWARFDataTypeImporter { } else { String memberComment = null; + boolean isDynamicSizedType = (childDT.dataType instanceof Dynamic || + childDT.dataType instanceof FactoryDataType); if (isDynamicSizedType) { memberComment = "Unsupported dynamic size data type: " + childDT.dataType; childDT.dataType = Undefined.getUndefinedDataType(1); } - int childLength = getUnpaddedDataTypeLength(childDT.dataType); - if (structure.isNotYetDefined() || - (memberOffset + childLength > structure.getLength())) { - // zero len struct can't have members added, even if they are zero len, or - // member is longer than struct has storage for + if (memberOffset + childLength > structure.getLength()) { DWARFUtil.appendDescription(structure, memberDesc("Missing member", "exceeds parent struct len", memberName, childDT, memberOffset, -1, -1), "\n"); @@ -999,21 +961,39 @@ public class DWARFDataTypeImporter { continue; } - DataTypeComponent existingDTC = structure.getComponentAt(memberOffset); - if (existingDTC != null && - !(existingDTC.getDataType() instanceof DefaultDataType)) { - DWARFUtil.appendDescription(structure, - memberDesc("Missing member", "conflict with " + existingDTC.getFieldName(), - memberName, childDT, memberOffset, -1, -1), - "\n"); - continue; - } try { - DataTypeComponent dtc = structure.replaceAtOffset(memberOffset, - childDT.dataType, childLength, memberName, memberComment); - - // struct.replaceAtOffset() clones the childDT, which will mess up our + DataTypeComponent dtc; + if (DataTypeComponent.usesZeroLengthComponent(childDT.dataType)) { + if (!isUndefinedOrZeroLenAtOffset(structure, memberOffset)) { + DWARFUtil.appendDescription(structure, memberDesc("Missing member", + "conflicting member at same offset", memberName, childDT, + memberOffset, -1, -1), "\n"); + continue; + } + // use insertAt for zero len members to allow multiple at same offset + dtc = + structure.insertAtOffset(memberOffset, childDT.dataType, 0, memberName, + memberComment); + } + else { + int ordinalToReplace = getUndefinedOrdinalAt(structure, memberOffset); + if (ordinalToReplace == -1) { + DataTypeComponent existingDTC = + structure.getComponentContaining(memberOffset); + if (existingDTC != null) { + DWARFUtil.appendDescription(structure, + memberDesc("Missing member", + "conflict with " + existingDTC.getFieldName(), + memberName, childDT, memberOffset, -1, -1), + "\n"); + } + continue; + } + dtc = structure.replace(ordinalToReplace, childDT.dataType, childLength, + memberName, memberComment); + } + // struct.replaceAtOffset() and insertAtOffset() clones the childDT, which will mess up our // identity based mapping in currentImplDataTypeToDDT. // Update the mapping to prevent that. updateMapping(childDT.dataType, dtc.getDataType()); @@ -1030,72 +1010,29 @@ public class DWARFDataTypeImporter { } } - private Set getConflictingZeroLenFields(DIEAggregate diea, int childTagType) - throws IOException, DWARFExpressionException { - // Returns a set of DIE offsets of zero len fields that are fighting for - // the same offset in the parent struct - Map> zeroLenMembers = new HashMap<>(); - - for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren(childTagType)) { - - DIEAggregate childDIEA = prog.getAggregate(childEntry); - if (childDIEA.hasAttribute(DWARFAttribute.DW_AT_external)) { - continue; - } - - int bitSize = childDIEA.parseInt(DWARFAttribute.DW_AT_bit_size, -1); - boolean isBitField = bitSize != -1; - if (isBitField) { - continue; - } - - DWARFDataType childDT = getDataType(childDIEA.getTypeRef(), null); - if (childDT == null) { - continue; - } - if (childDT.isZeroLenDT()) { - try { - int memberOffset = - childDIEA.parseDataMemberOffset(DWARFAttribute.DW_AT_data_member_location, - 0); - zeroLenMembers.computeIfAbsent(memberOffset, k -> new HashSet<>()) - .add(childEntry.getOffset()); - } - catch (DWARFExpressionException e) { - continue; - } - } + private boolean isUndefinedOrZeroLenAtOffset(Structure struct, int offset) { + List compsAt = struct.getComponentsContaining(offset); + DataTypeComponent lastComp = !compsAt.isEmpty() ? compsAt.get(compsAt.size() - 1) : null; + if (lastComp == null) { + // only triggered if offset == length of struct, which is okay since we are adding + // a zero-length component to the struct + return true; } - - Set conflictingZeroLenFields = new HashSet<>(); - for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren(childTagType)) { - DIEAggregate childDIEA = prog.getAggregate(childEntry); - if (childDIEA.hasAttribute(DWARFAttribute.DW_AT_external)) { - continue; - } - int bitSize = childDIEA.parseInt(DWARFAttribute.DW_AT_bit_size, -1); - boolean isBitField = bitSize != -1; - if (isBitField) { - continue; - } - - DWARFDataType childDT = getDataType(childDIEA.getTypeRef(), null); - if (childDT == null) { - continue; - } - int memberOffset = 0; - try { - memberOffset = - childDIEA.parseDataMemberOffset(DWARFAttribute.DW_AT_data_member_location, 0); - } - catch (DWARFExpressionException e) { - continue; - } - if (!childDT.isZeroLenDT() && zeroLenMembers.containsKey(memberOffset)) { - conflictingZeroLenFields.addAll(zeroLenMembers.get(memberOffset)); - } + if (lastComp.getOffset() != offset) { + return false; } - return conflictingZeroLenFields; + DataType dt = lastComp.getDataType(); + return dt.isZeroLength() || dt instanceof DefaultDataType; + } + + private int getUndefinedOrdinalAt(Structure struct, int offset) { + List compsAt = struct.getComponentsContaining(offset); + DataTypeComponent lastComp = !compsAt.isEmpty() ? compsAt.get(compsAt.size() - 1) : null; + if (lastComp == null || lastComp.getOffset() != offset || + !(lastComp.getDataType() instanceof DefaultDataType)) { + return -1; + } + return lastComp.getOrdinal(); } private static String memberDesc(String prefix, String errorStr, String memberName, @@ -1120,7 +1057,6 @@ public class DWARFDataTypeImporter { throws IOException, DWARFExpressionException { DWARFDataType elementType = getDataType(diea.getTypeRef(), voidDDT); - // do a second query to see if there was a recursive loop in the call above back // to this datatype that resulted in this datatype being created. // Use that instance if possible. @@ -1132,7 +1068,6 @@ public class DWARFDataTypeImporter { // Build a list of the defined dimensions for this array type. // The first element in the DWARF dimension list would be where a wild-card (-1 length) // dimension would be defined. - boolean isEmptyArray = false; List dimensions = new ArrayList<>(); List subrangeDIEs = diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_subrange_type); @@ -1146,17 +1081,14 @@ public class DWARFDataTypeImporter { } // Otherwise check for an upper bound else if (subrangeAggr.hasAttribute(DWARFAttribute.DW_AT_upper_bound)) { - // TODO: Check lower bound or get the difference based on - // the language (See DWARF4 Section 5.11) - // TODO: upperbound == -1 seems to indicate zero length array long upperBound = subrangeAggr.parseUnsignedLong(DWARFAttribute.DW_AT_upper_bound, 0xbadbeef); // fix special flag values used by DWARF to indicate that the array dimension - // is unknown. 64bit 0xffffff...s will already be -1, and 32bit 0xffff..s will - // be forced to -1. + // is unknown. 64bit 0xffffff...s and 32bit 0xffff..s will + // be forced to 0. if (upperBound == 0xFF_FF_FF_FFL /* ie. max uint32 */ || upperBound == -1) { - upperBound = -1; + upperBound = 0; } else { numElements = upperBound + 1; @@ -1169,28 +1101,7 @@ public class DWARFDataTypeImporter { } if (numElements == -1) { - // if numElements is the DWARF special flag value for unknown, set flag - // and force the value back to something that Ghidra datatypes can handle. - // The consumer of the resultant data type will get back an array with - // 1 element instead of 0 elements because of Ghidra's limitations. - // They should check the DWARFDataType.isEmptyArrayType flag (I'm looking at - // you makeDataTypeForStruct()) if they can handle unknown length arrays. - if (subRangeDIEIndex == 0) { - isEmptyArray = true; - } - else { - Msg.error(this, - "Bad undefined-length array dimension for subrange " + subRangeDIEIndex + - " for array's size in DIE: " + diea.getOffset() + ", forcing to 1"); - } - numElements = 1; - } - else if (numElements == 0) { - Msg.error(this, - "Unsupported value [" + numElements + "] for array's size in DIE: " + - diea.getOffset() + ", forcing to 1"); - numElements = 1; - isEmptyArray = true; + numElements = 0; } else if (numElements > Integer.MAX_VALUE) { Msg.error(this, "Bad value [" + numElements + "] for array's size in DIE: " + @@ -1205,7 +1116,7 @@ public class DWARFDataTypeImporter { for (int i = dimensions.size() - 1; i >= 0; i--) { int numElements = dimensions.get(i); ArrayDataType subArray = - new ArrayDataType(dt, numElements, dt.getLength(), dataTypeManager); + new ArrayDataType(dt, numElements, -1, dataTypeManager); if (dt == elementType.dataType) { updateMapping(dt, subArray.getDataType()); } @@ -1213,7 +1124,6 @@ public class DWARFDataTypeImporter { } DWARFDataType result = new DWARFDataType(dt, null, diea.getOffset()); - result.isEmptyArrayType = isEmptyArray; return result; } @@ -1445,7 +1355,6 @@ public class DWARFDataTypeImporter { static class DWARFDataType { DataType dataType; - boolean isEmptyArrayType; DWARFNameInfo dni; DWARFSourceInfo dsi; Set offsets = new HashSet<>(); @@ -1473,12 +1382,5 @@ public class DWARFDataTypeImporter { Collectors.joining(",")); } - boolean isZeroLenDT() { - DataType tmpDt = - (dataType instanceof TypeDef) ? ((TypeDef) dataType).getBaseDataType() : dataType; - return isEmptyArrayType || tmpDt.isNotYetDefined() || - tmpDt.getLength() == 0 /* this can't happen right now, but never know for future */; - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java index 10007ec58e..9e90e7c85e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java @@ -24,6 +24,7 @@ import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.util.bin.format.dwarf4.*; import ghidra.app.util.bin.format.dwarf4.encoding.*; import ghidra.app.util.bin.format.dwarf4.expression.*; +import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; @@ -258,6 +259,19 @@ public class DWARFFunctionImporter { DWARFTag.DW_TAG_formal_parameter)) { DIEAggregate childDIEA = prog.getAggregate(childEntry); + DataType childDT = dwarfDTM.getDataType(childDIEA.getTypeRef(), null); + if (childDT == null || DataTypeComponent.usesZeroLengthComponent(childDT)) { + String paramName = + childDIEA.getString(DWARFAttribute.DW_AT_name, "param" + formalParams.size()); + Msg.warn(this, "DWARF: zero-length function parameter " + paramName + + ":" + childDT.getName() + ", omitting from definition of " + + dfunc.dni.getName() + "@" + dfunc.address); + // skip this parameter because its data type is a zero-width type that typically does + // not generate code. If this varies compiler-to-compiler, setting + // skipFuncSignature=true may be a better choice + continue; + } + Parameter formalParam = createFormalParameter(childDIEA); if (formalParam == null) { skipFuncSignature = true; @@ -581,27 +595,7 @@ public class DWARFFunctionImporter { } } else if (exprEvaluator.getLastRegister() == null) { - dvar.dni = dvar.dni.replaceType(null /*nothing matches static global var*/); - if (res != 0) { - // If the expression evaluated to a static address other than '0' - Address staticVariableAddress = toAddr(res + prog.getProgramBaseAddressFixup()); - if (variablesProcesesed.contains(staticVariableAddress)) { - return null; - } - - boolean external = diea.getBool(DWARFAttribute.DW_AT_external, false); - - outputGlobal(staticVariableAddress, dvar.type, external, - DWARFSourceInfo.create(diea), dvar.dni); - } - else { - // If the expression evaluated to a static address of '0'. - // This case is probably caused by relocation fixups not being applied to the - // .debug_info section. - importSummary.relocationErrorVarDefs.add( - dvar.dni.getNamespacePath().asFormattedString() + " : " + - dvar.type.getPathName()); - } + processStaticVar(res, dvar, diea); return null;// Don't return the variable to be associated with the function } else { @@ -615,6 +609,61 @@ public class DWARFFunctionImporter { return dvar; } + private void processStaticVar(long address, DWARFVariable dvar, DIEAggregate diea) + throws InvalidInputException { + dvar.dni = dvar.dni.replaceType(null /*nothing matches static global var*/); + if (address != 0) { + Address staticVariableAddress = toAddr(address + prog.getProgramBaseAddressFixup()); + if (isZeroByteDataType(dvar.type)) { + processZeroByteStaticVar(staticVariableAddress, dvar); + return; + } + + if (variablesProcesesed.contains(staticVariableAddress)) { + return; + } + + boolean external = diea.getBool(DWARFAttribute.DW_AT_external, false); + + outputGlobal(staticVariableAddress, dvar.type, external, + DWARFSourceInfo.create(diea), dvar.dni); + } + else { + // If the expression evaluated to a static address of '0'. + // This case is probably caused by relocation fixups not being applied to the + // .debug_info section. + importSummary.relocationErrorVarDefs.add( + dvar.dni.getNamespacePath().asFormattedString() + " : " + + dvar.type.getPathName()); + } + } + + private void processZeroByteStaticVar(Address staticVariableAddress, DWARFVariable dvar) + throws InvalidInputException { + // because this is a zero-length data type (ie. array[0]), + // don't create a variable at the location since it will prevent other elements + // from occupying the same offset + Listing listing = currentProgram.getListing(); + String comment = + listing.getComment(CodeUnit.PRE_COMMENT, staticVariableAddress); + comment = (comment != null) ? comment + "\n" : ""; + comment += String.format("Zero length variable: %s: %s", dvar.dni.getOriginalName(), + dvar.type.getDisplayName()); + listing.setComment(staticVariableAddress, CodeUnit.PRE_COMMENT, comment); + + SymbolTable symbolTable = currentProgram.getSymbolTable(); + symbolTable.createLabel(staticVariableAddress, dvar.dni.getName(), + dvar.dni.getParentNamespace(currentProgram), + SourceType.IMPORTED); + } + + private boolean isZeroByteDataType(DataType dt) { + if (!dt.isZeroLength() && dt instanceof Array) { + dt = DataTypeUtilities.getArrayBaseDataType((Array) dt); + } + return dt.isZeroLength(); + } + /** * Process lexical block entries. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java index 6850f3f2ad..7d74a0bf4c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java @@ -108,9 +108,8 @@ public class PEx64UnwindInfoDataType extends DynamicDataType { if (hasExceptionHandler(flags) || hasUnwindHandler(flags)) { struct.add(IBO32, "ExceptionHandler", null); if (hasUnwindHandler(flags)) { - // NOTE: Dynamic structure does not reflect flex-array - struct.setFlexibleArrayComponent(UnsignedLongDataType.dataType, "ExceptionData", - null); + struct.add(new ArrayDataType(UnsignedLongDataType.dataType, 0, -1), + "ExceptionData", null); } } else if (hasChainedUnwindInfo(flags)) { @@ -132,14 +131,7 @@ public class PEx64UnwindInfoDataType extends DynamicDataType { if (struct == null) { return null; } - DataTypeComponent[] components = struct.getComponents(); - if (struct.hasFlexibleArrayComponent()) { - DataTypeComponent[] newArray = new DataTypeComponent[components.length + 1]; - System.arraycopy(components, 0, newArray, 0, components.length); - newArray[components.length] = struct.getFlexibleArrayComponent(); - components = newArray; - } - return components; + return struct.getComponents(); } private boolean hasExceptionHandler(int flags) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java index bf9ad61042..746b4cca6a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CompositeHandler.java @@ -43,17 +43,8 @@ public class CompositeHandler { if (dec == null || dec.getDataType() == null) { return; } - if (parent instanceof Structure) { - // ensure that only the last component establishes a structure's flex array - ((Structure) parent).clearFlexibleArrayComponent(); - } // not a bitfield, just add the data type to composite if (!dec.isBitField()) { - if (dec.isFlexArray() && parent instanceof Structure) { - ((Structure) parent).setFlexibleArrayComponent(dec.getDataType(), dec.getName(), - dec.getComment()); - return; - } parent.add(dec.getDataType(), dec.getName(), dec.getComment()); return; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java index 437bc82035..bbd3979ca4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/Declaration.java @@ -27,7 +27,6 @@ public class Declaration { private String name; private String comment; private int bitSize = -1; - private boolean flexArray = false; // true if this is a zero size flex array component public Declaration() { super(); @@ -155,11 +154,4 @@ public class Declaration { bitSize = bits; } - public void setFlexArray(boolean b) { - flexArray = b; - } - - public boolean isFlexArray() { - return flexArray; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/ArrayDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/ArrayDataTypeHTMLRepresentation.java index 20359e93f2..1df5c10716 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/ArrayDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/ArrayDataTypeHTMLRepresentation.java @@ -110,7 +110,11 @@ public class ArrayDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation } private ValidatableLine buildFooterContent() { - return new TextLine("Size: " + array.getLength()); + int len = array.getLength(); + if (array.isZeroLength()) { + return new TextLine("Size: 0 (reported size is " + len + ")"); + } + return new TextLine("Size: " + len); } private String buildHTMLText(ValidatableLine header, String body, ValidatableLine info, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java index f893309645..6f1b2e9e61 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java @@ -100,11 +100,11 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat protected List buildAlignmentText(Composite dataType) { List list = new ArrayList<>(); - String alignStr = CompositeDataTypeImpl.getMinAlignmentString(dataType); + String alignStr = CompositeInternal.getMinAlignmentString(dataType); if (alignStr != null && alignStr.length() != 0) { list.add(new TextLine(alignStr)); } - String packStr = CompositeDataTypeImpl.getPackingString(dataType); + String packStr = CompositeInternal.getPackingString(dataType); if (packStr != null && packStr.length() != 0) { list.add(new TextLine(packStr)); } @@ -117,10 +117,6 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat private List buildContent(Composite comp) { List list = new ArrayList<>(); - if (comp.isZeroLength()) { - return list; - } - int count = 0; DataTypeComponent[] components = comp.getComponents(); for (DataTypeComponent dataTypeComponent : components) { @@ -135,27 +131,14 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat locatableType = getLocatableDataType(dataType); } - list.add(new DataTypeLine(fieldName, type, comment, locatableType, false)); + list.add(new DataTypeLine(fieldName, type, comment, locatableType)); if (count++ >= MAX_COMPONENT_COUNT) { // Prevent a ridiculous number of components from consuming all memory. - list.add(new DataTypeLine("", "Warning: Too many components to display...", "", - null, false)); + list.add( + new DataTypeLine("", "Warning: Too many components to display...", "", null)); break; } } - if (comp instanceof Structure) { - Structure struct = (Structure) comp; - DataTypeComponent flexibleArrayComponent = struct.getFlexibleArrayComponent(); - if (count < MAX_COMPONENT_COUNT && flexibleArrayComponent != null) { - String fieldName = flexibleArrayComponent.getFieldName(); - String comment = flexibleArrayComponent.getComment(); - DataType dataType = flexibleArrayComponent.getDataType(); - String type = dataType.getDisplayName(); - DataType locatableType = getLocatableDataType(dataType); - list.add(new DataTypeLine(fieldName, type, comment, locatableType, true)); - - } - } return list; } @@ -239,9 +222,6 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat StringBuilder lineBuffer = new StringBuilder(); DataTypeLine line = (DataTypeLine) iterator.next(); String typeName = generateTypeName(line, trim); - if (line.isFlexibleArray()) { - typeName += "[0]"; - } int fieldLength = ToolTipUtils.LINE_LENGTH / 2; String fieldName = line.getName(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/DataTypeLine.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/DataTypeLine.java index 7d3a3e0b8d..97d90eb725 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/DataTypeLine.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/DataTypeLine.java @@ -29,7 +29,6 @@ public class DataTypeLine implements ValidatableLine { private String name; private String comment; private DataType dataType; - private boolean isFlexibleArray; private Color typeColor; private Color nameColor; @@ -37,9 +36,8 @@ public class DataTypeLine implements ValidatableLine { private ValidatableLine validationLine; - DataTypeLine(String name, String type, String comment, DataType dt, boolean isFlexibleArray) { + DataTypeLine(String name, String type, String comment, DataType dt) { this.dataType = dt; - this.isFlexibleArray = isFlexibleArray; if (name == null) { name = ""; } @@ -49,17 +47,9 @@ public class DataTypeLine implements ValidatableLine { this.comment = comment == null ? "" : comment; } - /** - * Determine if data type should be treated as flexible array - * @return true if data type should be treated as flexible array - */ - public boolean isFlexibleArray() { - return isFlexibleArray; - } - @Override public ValidatableLine copy() { - return new DataTypeLine(name, type, comment, dataType, isFlexibleArray); + return new DataTypeLine(name, type, comment, dataType); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EmptyDataTypeLine.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EmptyDataTypeLine.java index 517d4bd08c..99d8f1306f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EmptyDataTypeLine.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EmptyDataTypeLine.java @@ -21,7 +21,7 @@ import ghidra.util.exception.AssertException; public class EmptyDataTypeLine extends DataTypeLine implements PlaceHolderLine { public EmptyDataTypeLine() { - super("", "", "", null, false); + super("", "", "", null); } @Override 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 7fba5ce4e6..e7483cc766 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 @@ -20,6 +20,8 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldUtils; @@ -245,19 +247,32 @@ public class PreCommentFieldFactory extends FieldFactory { } private String[] getDataAutoComments(Data data) { - - // Build flexible array comment - Address addr = data.getMinAddress().previous(); - if (addr != null) { - return getFlexArrayComment(data, addr); - } - return null; + return getPreceedingComponentAutoComment(data); } - private String[] getFlexArrayComment(Data data, Address addr) { + /** + * A composite which immediately preceeds the current address may contain trailing zero-length + * components which implicitly refer to this address and are not rendered by the opened composite. + * This comment is intended to convey the existence of such hidden components which correspond + * to addr. + *
+ * NOTE: Implementation only provides comment for one trailing zero-length component. This could + * be improved to return a comment for all applicable trailing zero-length components. + * @param data data location whose pre-comment is currently be generated + * @return auto-comment or null + */ + private String[] getPreceedingComponentAutoComment(Data data) { + // NOTE: A zero-length composite has a length of 1 which may cause it to improperly consume + // the address location which actually corresponds to a trailing zero-length + // component. + int levelsToIgnore = 0; String label = null; + Address prevDataAddr = data.getMinAddress().previous(); + if (prevDataAddr == null) { + return null; + } int[] cpath = data.getComponentPath(); if (cpath != null && cpath.length > 0) { @@ -273,46 +288,60 @@ public class PreCommentFieldFactory extends FieldFactory { } else { Program p = data.getProgram(); - data = p.getListing().getDefinedDataContaining(addr); - if (data == null || !(data.isStructure() || data.isDynamic())) { + data = p.getListing().getDefinedDataContaining(prevDataAddr); + if (data == null || !(data.isStructure() || data.isDynamic())) { // FIXME!! refer to DynamicDataType which has components - Union? return null; } Symbol s = p.getSymbolTable().getPrimarySymbol(data.getAddress()); label = s != null ? s.getName(true) : data.getDataType().getName(); } - // locate deepest structure containing addr which will be checked for flex array + DataTypeComponent lastDtc = null; while (true) { - int offset = (int) addr.subtract(data.getMinAddress()); - Data component = data.getComponentAt(offset); - if (component == null || !component.isStructure()) { + DataType dt = data.getDataType(); + + 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); + } + } + 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); + } + } + + if (lastDtc == null || lastDtc.getLength() == 0) { break; } + + Data component = data.getComponent(lastDtc.getOrdinal()); + if (component == null) { + return null; + } data = component; } - return buildFlexArrayComment(data, levelsToIgnore, label); - } - - private String[] buildFlexArrayComment(Data data, int levelsToIgnore, String label) { - - DataType dt = data.getBaseDataType(); - - DataTypeComponent flexComponent = null; - if (dt instanceof Structure) { - flexComponent = ((Structure) dt).getFlexibleArrayComponent(); - } - else if (dt instanceof DynamicDataType) { - flexComponent = ((DynamicDataType) dt).getFlexibleArrayComponent(data); - } - - if (flexComponent == null) { + if (lastDtc == null || lastDtc.isBitFieldComponent()) { return null; } + + return buildZeroLengthComponentAutoComment(lastDtc, data, levelsToIgnore, label); + } - String fieldName = flexComponent.getFieldName(); - if (fieldName == null) { - fieldName = flexComponent.getDefaultFieldName(); + private String[] buildZeroLengthComponentAutoComment(DataTypeComponent lastZeroLengthComponent, Data data, int levelsToIgnore, String label) { + + String fieldName = lastZeroLengthComponent.getFieldName(); + if (StringUtils.isEmpty(fieldName)) { + fieldName = lastZeroLengthComponent.getDefaultFieldName(); } StringBuilder flexName = new StringBuilder(fieldName); @@ -331,8 +360,8 @@ public class PreCommentFieldFactory extends FieldFactory { flexName.insert(0, label + "."); } - return new String[] { "Flexible Array: " + flexComponent.getDataType().getName() + "[] " + - flexName.toString() }; + return new String[] { "Zero-length Component: " + lastZeroLengthComponent.getDataType().getName() + " " + + flexName.toString() }; } private ListingTextField getTextField(String[] comments, String[] autoComment, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java index 02497de3f4..4f2ffbc224 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ProgramBigListingModel.java @@ -414,11 +414,11 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener } } } - else { // Structure + else { // Structure and DynamicDataType List dataList = data.getComponentsContaining((int) addr.subtract(dataAddr)); - if (dataList != null) { // nested flex-arrays can cause odd behavior + if (dataList != null) { for (Data subData : dataList) { - // The only case where more than one subData exists is for bit-fields. + // The only case where more than one subData exists is for bit-fields and zero-length data. // Depending upon the packing, bit-fields at different offsets may overlap if (subData.getMinAddress().equals(addr)) { list.add(subData); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java index f541e4bf45..3854dcc187 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java @@ -649,7 +649,8 @@ public class DataTypesXmlMgr { private void writerMember(XmlWriter writer, DataTypeComponent member) { XmlAttributes attrs = new XmlAttributes(); - // TODO: how should we output bitfields (packed/non-packed) and flex array + // TODO: how should we output bitfields (packed/non-packed) + // TODO: multiple components at same offset (e.g., zero-length arrays) could throw-off IDA XML import attrs.addAttribute("OFFSET", member.getOffset(), true); attrs.addAttribute("DATATYPE", member.getDataType().getDisplayName()); attrs.addAttribute("DATATYPE_NAMESPACE", member.getDataType().getCategoryPath().getPath()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java index 4124eeec9a..bec59a8fdc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java @@ -1032,7 +1032,7 @@ public class CodeUnitFormat { DataType dataType = data.getDataType(); int length = data.getLength(); - if (length < dataType.getLength()) { + if ((length != 0 || !dataType.isZeroLength()) && dataType.getLength() > length) { representationList.add("Data type \"" + dataType.getDisplayName() + "\" is too big for available space. Size = " + dataType.getLength() + " bytes, available = " + length + " bytes"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java index ed790f431d..fae1b0a760 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffDetails.java @@ -882,22 +882,6 @@ public class ProgramDiffDetails { for (int index2 = i; index2 < compDt2.length; index2++) { getComponentInfo(compDt2[index2], buf2, newIndent); } - - if (dt1 instanceof Structure) { - // dt2 is also Structure - check for flex array component - DataTypeComponent flexDtc1 = ((Structure) dt1).getFlexibleArrayComponent(); - DataTypeComponent flexDtc2 = ((Structure) dt2).getFlexibleArrayComponent(); - if (flexDtc1 != null) { - getComponentInfo(flexDtc1, buf1, newIndent); - } - if (flexDtc2 != null) { - getComponentInfo(flexDtc2, buf2, newIndent); - } - if (flexDtc1 != null && flexDtc2 != null) { - compareSubDataTypes(flexDtc1.getDataType(), flexDtc2.getDataType(), buf1, buf2, - newIndent); - } - } } private void compareDataCUs(Data d1, Data d2, StringBuffer buf1, StringBuffer buf2, @@ -926,22 +910,13 @@ public class ProgramDiffDetails { if (fieldName == null) { fieldName = dtc.getDefaultFieldName(); } - if (dtc.isFlexibleArrayComponent()) { - buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " + - "Ordinal=" + ordinal + " " + fieldName + " " + - actualDt.getMnemonic(actualDt.getDefaultSettings()) + "[]" + " " + - getCategoryName(actualDt) + " " + "DataTypeSize=" + actualDt.getLength() + - " (flexible array) " + ((comment != null) ? comment : "") + " " + newLine); - } - else { - // TODO: how should we display bitfields? - buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " + - "Ordinal=" + ordinal + " " + fieldName + " " + - actualDt.getMnemonic(actualDt.getDefaultSettings()) + " " + - getCategoryName(actualDt) + " " + "DataTypeSize=" + actualDt.getLength() + " " + - "ComponentSize=" + dtc.getLength() + " " + ((comment != null) ? comment : "") + - " " + newLine); - } + // TODO: how should we display bitfields? + buf.append(indent + "Offset=" + DiffUtility.toSignedHexString(offset) + " " + "Ordinal=" + + ordinal + " " + fieldName + " " + actualDt.getMnemonic(actualDt.getDefaultSettings()) + + " " + getCategoryName(actualDt) + " " + "DataTypeSize=" + + (actualDt.isZeroLength() ? 0 : actualDt.getLength()) + " " + "ComponentSize=" + + dtc.getLength() + " " + ((comment != null) ? comment : "") + + " " + newLine); return actualDt; } @@ -983,18 +958,17 @@ public class ProgramDiffDetails { Data data = (Data) cu; DataType dt = data.getDataType(); if (dt instanceof Composite) { - DataTypeComponent[] compDt = ((Composite) dt).getComponents(); - for (DataTypeComponent element : compDt) { - int offset = element.getOffset(); - String comment = element.getComment(); - String fieldName = element.getFieldName(); + DataTypeComponent[] components = ((Composite) dt).getComponents(); + for (DataTypeComponent dtc : components) { + int offset = dtc.getOffset(); + String comment = dtc.getComment(); + String fieldName = dtc.getFieldName(); if (fieldName == null) { fieldName = "field" + offset; } - buf.append(newIndent + min.add(offset) + " " + element.getFieldName() + - " " + element.getDataType().getName() + " " + "length=" + - element.getLength() + " " + - + buf.append(newIndent + min.add(offset) + " " + dtc.getFieldName() + " " + + dtc.getDataType().getName() + " " + "length=" + + dtc.getLength() + " " + ((comment != null) ? comment : "") + " " + newLine); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java b/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java index e46cfd75dd..1a76330622 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java @@ -573,9 +573,16 @@ public class DataTypeParser { ArraySpecPiece(String piece) throws InvalidDataTypeException { if (piece.startsWith("[") && piece.endsWith("]")) { - String elementCountStr = piece.substring(1, piece.length() - 1); try { - elementCount = parseSize(elementCountStr); + String elementCountStr = piece.substring(1, piece.length() - 1); + if (elementCountStr.length() == 0) { + // treat empty array spec same as 0 + // consumer may need to handle resulting array as a pointer (e.g., parameter) + elementCount = 0; + } + else { + elementCount = parseSize(elementCountStr); + } return; } catch (NumberFormatException e) { diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index f0fb377b0c..db7ec56c95 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -1968,14 +1968,7 @@ Declaration DirectDeclarator(Declaration dt, DataType container) : { for (Iterator iterator = list.iterator(); iterator.hasNext();) { Integer iSize = (Integer) iterator.next(); DataType decDt = dec.getDataType(); - if (iSize == 0 && container != null) { - dec.setFlexArray(true); - break; - } else if (iSize == 0) { - dec.setDataType(dtMgr.getPointer(decDt)); - } else { - dec.setDataType(new ArrayDataType(decDt, iSize, decDt.getLength())); - } + dec.setDataType(new ArrayDataType(decDt, iSize, decDt.getLength())); } //System.out.println("Array expr: for " + dec.getName() + " make an array " + dt.getName() + "["+size+"]"); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge1Test.java index eb2f312c7e..1b8dac9c7f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge1Test.java @@ -27,7 +27,7 @@ import ghidra.program.database.ProgramModifierListener; import ghidra.program.model.data.*; import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; /** * Tests for merging data types. @@ -66,12 +66,11 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { struct.insertBitFieldAt(3, 2, 6, td, 2, "bf1", null); struct.insertBitFieldAt(3, 2, 4, td, 2, "bf2", null); struct.add(new QWordDataType()); - - struct.setFlexibleArrayComponent(td, "flex", "my flex"); + struct.add(new ArrayDataType(td, 0, -1), 0, "flex", "my flex"); structRef.set(struct); - c.removeCategory("Category5", TaskMonitorAdapter.DUMMY); + c.removeCategory("Category5", TaskMonitor.DUMMY); Category c5 = c.createCategory("Category5"); c5.addDataType(struct, DataTypeConflictHandler.DEFAULT_HANDLER); commit = true; @@ -187,7 +186,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); try { Structure s = (Structure) c.getDataType("IntStruct"); - c.remove(s, TaskMonitorAdapter.DUMMY); + c.remove(s, TaskMonitor.DUMMY); s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0, dtm); s.add(new QWordDataType(), "f1", "my f1"); s.add(new FloatDataType()); @@ -220,7 +219,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dt; assertEquals("my f1", s.getComponent(0).getComment()); - DataTypeComponent dtc = s.getComponentAt(17); + DataTypeComponent dtc = s.getComponentContaining(17); assertEquals(7, dtc.getOrdinal()); assertEquals("my bf1", dtc.getComment()); } @@ -243,7 +242,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); try { Structure s = (Structure) c.getDataType("IntStruct"); - c.remove(s, TaskMonitorAdapter.DUMMY); + c.remove(s, TaskMonitor.DUMMY); s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0); s.add(new QWordDataType()); s.add(new FloatDataType()); @@ -440,7 +439,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -479,7 +478,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { try { DataType dt = dtm.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER); - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -530,7 +529,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -581,7 +580,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "FloatStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -611,7 +610,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -659,7 +658,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -715,7 +714,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -734,7 +733,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -963,7 +962,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -992,7 +991,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY); + dtm.remove(dt, TaskMonitor.DUMMY); commit = true; } finally { @@ -1050,7 +1049,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dt; s.add(new ByteDataType()); Category parent = dtm.getCategory(new CategoryPath("/Category1/Category2")); - parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY); + parent.removeCategory("Category3", TaskMonitor.DUMMY); commit = true; } finally { @@ -1116,7 +1115,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dt; s.add(new ByteDataType()); Category parent = dtm.getCategory(new CategoryPath("/Category1/Category2")); - parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY); + parent.removeCategory("Category3", TaskMonitor.DUMMY); commit = true; } finally { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java index d97a996841..fb487f4186 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java @@ -1097,7 +1097,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); - s1.setFlexibleArrayComponent(td, null, null); + s1.add(new ArrayDataType(td, 0, -1), 0, null, null); } finally { program.endTransaction(transactionID, true); @@ -1112,7 +1112,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); - s1.setFlexibleArrayComponent(IntegerDataType.dataType, "flex1", "cmt1"); + s1.add(new ArrayDataType(IntegerDataType.dataType, 0, -1), "flex1", "cmt1"); } finally { program.endTransaction(transactionID, true); @@ -1134,11 +1134,15 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); assertNotNull(s1); DataTypeComponent[] dtcs = s1.getComponents(); - assertEquals(4, dtcs.length); + assertEquals(5, dtcs.length); - DataTypeComponent flexDtc = s1.getFlexibleArrayComponent(); - assertNotNull(flexDtc); - assertTrue(IntegerDataType.class == flexDtc.getDataType().getClass()); + DataTypeComponent flexDtc = s1.getComponent(4); + assertEquals(0, flexDtc.getLength()); + DataType dt = flexDtc.getDataType(); + assertTrue(dt instanceof Array); + Array a = (Array) dt; + assertEquals(0, a.getNumElements()); + assertTrue(a.getDataType() instanceof IntegerDataType); assertEquals("flex1", flexDtc.getFieldName()); assertEquals("cmt1", flexDtc.getComment()); } @@ -1159,7 +1163,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); - s1.setFlexibleArrayComponent(IntegerDataType.dataType, null, null); + s1.add(new ArrayDataType(IntegerDataType.dataType, 0, -1), 0, null, null); } finally { program.endTransaction(transactionID, true); @@ -1174,7 +1178,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); - s1.setFlexibleArrayComponent(td, "flex1", "cmt1"); + // last component is flex array to be replaced + s1.replace(s1.getNumComponents() - 1, new ArrayDataType(td, 0, -1), 0, "flex1", + "cmt1"); } finally { program.endTransaction(transactionID, true); @@ -1189,9 +1195,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); + s1.deleteAtOffset(s1.getLength()); s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1"); s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2"); - s1.clearFlexibleArrayComponent(); } catch (InvalidDataTypeException e) { e.printStackTrace(); @@ -1217,8 +1223,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); assertNotNull(s1); - DataTypeComponent flexDtc = s1.getFlexibleArrayComponent(); - assertNull(flexDtc); + for (DataTypeComponent dtc : s1.getComponents()) { + assertNotEquals(0, dtc.getLength()); + } DataTypeComponent[] dtcs = s1.getComponents(); assertEquals(7, dtcs.length); @@ -1260,8 +1267,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { int transactionID = program.startTransaction("test"); try { Structure s = (Structure) dtm.getDataType("/Category5/Test"); - DataType dt = dtm.getDataType("/MISC/FooTypedef"); - s.setFlexibleArrayComponent(dt, "foo", ""); + DataType td = dtm.getDataType("/MISC/FooTypedef"); + s.replaceAtOffset(s.getLength(), new ArrayDataType(td, 0, -1), 0, "foo", null); commit = true; } finally { @@ -1310,8 +1317,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { struct.insertBitFieldAt(3, 2, 4, td, 2, "bf2", null); struct.add(new WordDataType()); struct.add(new QWordDataType()); - - struct.setFlexibleArrayComponent(td, "flex", "my flex"); + struct.add(new ArrayDataType(td, 0, -1), 0, "flex", "my flex"); dtm.addDataType(struct, null); commit = true; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java index 24005deab7..f754a46413 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java @@ -55,14 +55,15 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes addDataType(ByteDataType.dataType); addDataType(FloatDataType.dataType); - addFlexDataType(DWordDataType.dataType, null, null); + addFlexDataType((Structure) structureModel.viewComposite, DWordDataType.dataType, null, + null); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 4, "float", FloatDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, 5, 0, "ddw[0]", DWordDataType.dataType, "", ""); + checkFlexArrayRow(2, 5, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(5); assertActualAlignment(1); } @@ -73,16 +74,17 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes addDataType(ByteDataType.dataType); addDataType(CharDataType.dataType); - addFlexDataType(DWordDataType.dataType, null, null); + addFlexDataType((Structure) structureModel.viewComposite, DWordDataType.dataType, null, + null); waitForSwing(); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, 2, 0, "ddw[0]", DWordDataType.dataType, "", ""); + checkFlexArrayRow(2, 2, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(2); assertActualAlignment(1); @@ -90,12 +92,12 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes assertIsPackingEnabled(true); assertDefaultPacked(); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, 4, 0, "ddw[0]", DWordDataType.dataType, "", ""); + checkFlexArrayRow(2, 4, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(4); assertActualAlignment(4); } @@ -106,7 +108,8 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes addDataType(ByteDataType.dataType); addDataType(CharDataType.dataType); - addFlexDataType(DWordDataType.dataType, null, null); + addFlexDataType((Structure) structureModel.viewComposite, DWordDataType.dataType, null, + null); waitForSwing(); @@ -117,12 +120,14 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes pressButtonByName(getPanel(), "Machine Alignment"); assertIsMachineAligned(); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, 8, 0, "ddw[0]", DWordDataType.dataType, "", ""); + // It is important to note that a trailing flex array will align the same as any other component and + // is not guarenteed to fall at the end of the structure. + checkFlexArrayRow(2, 4, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(8); assertActualAlignment(8); } @@ -135,7 +140,8 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes addDataType(ByteDataType.dataType); addDataType(CharDataType.dataType); - addFlexDataType(DWordDataType.dataType, null, null); + addFlexDataType((Structure) structureModel.viewComposite, DWordDataType.dataType, null, + null); waitForSwing(); @@ -156,51 +162,49 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes assertEquals(false, structureModel.viewComposite.isMachineAligned()); assertEquals(8, structureModel.getExplicitMinimumAlignment()); - assertEquals(2, structureModel.getNumComponents()); - assertEquals(4, structureModel.getRowCount()); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, 8, 0, "ddw[0]", DWordDataType.dataType, "", ""); + checkFlexArrayRow(2, 4, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(8); assertActualAlignment(8); } @Test public void testByValue1AlignedStructure() throws Exception { - checkByValueAlignedStructure(1, 4, 4); + checkByValueAlignedStructure(1, 4, 4, 4); } @Test public void testByValue2AlignedStructure() throws Exception { - checkByValueAlignedStructure(2, 4, 4); + checkByValueAlignedStructure(2, 4, 4, 4); } @Test public void testByValue4AlignedStructure() throws Exception { - checkByValueAlignedStructure(4, 4, 4); + checkByValueAlignedStructure(4, 4, 4, 4); } @Test public void testByValue8AlignedStructure() throws Exception { - checkByValueAlignedStructure(8, 8, 8); + checkByValueAlignedStructure(8, 8, 8, 4); } @Test public void testByValue16AlignedStructure() throws Exception { - checkByValueAlignedStructure(16, 16, 16); + checkByValueAlignedStructure(16, 16, 16, 4); } - public void checkByValueAlignedStructure(int value, int alignment, int length) + public void checkByValueAlignedStructure(int value, int alignment, int length, int flexOffset) throws Exception { emptyStructure.setPackingEnabled(true); emptyStructure.setExplicitMinimumAlignment(value); emptyStructure.add(ByteDataType.dataType); emptyStructure.add(CharDataType.dataType); - emptyStructure.setFlexibleArrayComponent(DWordDataType.dataType, null, null); + addFlexDataType(emptyStructure, DWordDataType.dataType, null, null); init(emptyStructure, pgmRootCat, false); CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); @@ -219,12 +223,13 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes assertEquals(false, structureModel.viewComposite.isMachineAligned()); assertEquals(value, structureModel.getExplicitMinimumAlignment()); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, length, 0, "ddw[0]", DWordDataType.dataType, "", ""); + + checkFlexArrayRow(2, flexOffset, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(length); assertActualAlignment(alignment); } @@ -240,7 +245,7 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes addDataType(ByteDataType.dataType); addDataType(CharDataType.dataType); - addFlexDataType(DWordDataType.dataType, null, null); + addFlexDataType((Structure) structureModel.viewComposite, DWordDataType.dataType, null, null); JRadioButton byValuePackingButton = (JRadioButton) findComponentByName(editorPanel, "Explicit Packing"); @@ -255,18 +260,24 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes assertEquals(false, structureModel.viewComposite.isMachineAligned()); assertEquals(false, structureModel.viewComposite.hasExplicitMinimumAlignment()); - assertEquals(2, structureModel.getNumComponents()); + assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); - checkBlankRow(2); - checkRow(3, 2, 0, "ddw[0]", DWordDataType.dataType, "", ""); + checkFlexArrayRow(2, 2, "ddw", DWordDataType.dataType, "", ""); + checkBlankRow(3); assertLength(2); assertActualAlignment(1); } //////////////////////////// + private void checkFlexArrayRow(int rowIndex, int offset, String mnemonic, DataType dataType, + String name, String comment) { + ArrayDataType a = new ArrayDataType(dataType, 0, -1); + checkRow(rowIndex, offset, 0, mnemonic + "[0]", a, name, comment); + } + private void checkRow(int rowIndex, int offset, int length, String mnemonic, DataType dataType, String name, String comment) { assertTrue(dataType.isEquivalent(structureModel.getComponent(rowIndex).getDataType())); @@ -294,9 +305,9 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes return structureModel.viewComposite.add(dataType); } - private DataTypeComponent addFlexDataType(DataType dataType, String name, String comment) { - return ((Structure) structureModel.viewComposite).setFlexibleArrayComponent(dataType, name, - comment); + private DataTypeComponent addFlexDataType(Structure struct, DataType dataType, String name, String comment) { + ArrayDataType a = new ArrayDataType(dataType, 0, 1); + return struct.add(a, name, comment); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java index 030538916b..99c7b744ea 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java @@ -51,7 +51,7 @@ public class StructureEditorLockedCellEditTest extends AbstractStructureEditorTe try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Structure) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java index ab011f5bf1..7532cbb2b1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java @@ -55,6 +55,7 @@ public class StructureEditorUnlockedActions4Test invoke(applyAction); assertTrue(complexStructure.isEquivalent(model.viewComposite)); assertEquals(1, complexStructure.getLength()); + assertTrue(complexStructure.isNotYetDefined()); assertTrue(complexStructure.isZeroLength()); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java index 0ca919016d..beda61d47f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java @@ -230,7 +230,10 @@ public class StructureEditorUnlockedActions5Test triggerEnter(textField); assertEquals(originalLength, model.getLength()); + assertFalse(applyAction.isEnabled()); + setErrorsExpected(true); invoke(applyAction); + setErrorsExpected(false); assertTrue(complexStructure.isEquivalent(model.viewComposite)); assertEquals(originalLength, complexStructure.getLength()); } @@ -590,7 +593,10 @@ public class StructureEditorUnlockedActions5Test triggerActionKey(component, 0, KeyEvent.VK_ENTER); assertEquals(newLength, model.getLength()); + assertFalse(applyAction.isEnabled()); + setErrorsExpected(true); invoke(applyAction); + setErrorsExpected(false); assertTrue(complexStructure.isEquivalent(model.viewComposite)); assertEquals(newLength, complexStructure.getLength()); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java index 63dc787c74..de64a0cfb8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java @@ -52,6 +52,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { DataType dt = pgmRootCat.getDataType(emptyStructure.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertIsPackingEnabled(false); @@ -93,6 +94,9 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertStatus(""); deleteAllComponents(); + + assertTrue(model.viewComposite.isNotYetDefined()); + assertTrue(model.viewComposite.isZeroLength()); assertEquals(0, model.getNumComponents()); assertEquals(1, model.getRowCount()); @@ -116,10 +120,12 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { invoke(applyAction); assertTrue(simpleStructure.isEquivalent(model.viewComposite)); + assertTrue(simpleStructure.isNotYetDefined()); assertTrue(simpleStructure.isZeroLength()); dt = pgmBbCat.getDataType(simpleStructure.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertIsPackingEnabled(false); @@ -214,6 +220,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertIsPackingEnabled(false); @@ -309,9 +316,11 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(innerStructure.isEquivalent(model.viewComposite)); + assertTrue(innerStructure.isNotYetDefined()); assertTrue(innerStructure.isZeroLength()); assertIsPackingEnabled(false); @@ -350,7 +359,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { } assertNotNull(innerStructure); assertNotNull(innerTypedef); - assertTrue(!innerTypedef.isZeroLength()); + assertFalse(innerTypedef.isZeroLength()); init(innerStructure, pgmTestCat, false); @@ -402,9 +411,11 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(innerStructure.isEquivalent(model.viewComposite)); + assertTrue(innerStructure.isNotYetDefined()); assertTrue(innerStructure.isZeroLength()); assertIsPackingEnabled(false); @@ -417,6 +428,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(false, applyAction.isEnabled()); // assertStatus("/testCat/innerStructure is contained in /testCat/innerStructureTypedef and can't be changed to a zero size data type."); + assertFalse(innerTypedef.isNotYetDefined()); // Typedef is always defined even if referenced type is not assertTrue(innerTypedef.isZeroLength()); assertEquals(1, innerTypedef.getLength()); } @@ -498,9 +510,11 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(innerStructure.isEquivalent(model.viewComposite)); + assertTrue(innerStructure.isNotYetDefined()); assertTrue(innerStructure.isZeroLength()); assertIsPackingEnabled(false); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java index 9e749ea549..83da5b8ea8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java @@ -52,6 +52,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmRootCat.getDataType(emptyUnion.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertIsPackingEnabled(false); @@ -116,10 +117,12 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { invoke(applyAction); assertTrue(simpleUnion.isEquivalent(model.viewComposite)); + assertTrue(simpleUnion.isNotYetDefined()); assertTrue(simpleUnion.isZeroLength()); dt = pgmBbCat.getDataType(simpleUnion.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(simpleUnion.isEquivalent(model.viewComposite)); @@ -214,6 +217,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertIsPackingEnabled(false); @@ -307,9 +311,11 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(innerUnion.isEquivalent(model.viewComposite)); + assertTrue(innerUnion.isNotYetDefined()); assertTrue(innerUnion.isZeroLength()); assertIsPackingEnabled(false); @@ -397,9 +403,11 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(innerUnion.isEquivalent(model.viewComposite)); + assertTrue(innerUnion.isNotYetDefined()); assertTrue(innerUnion.isZeroLength()); assertIsPackingEnabled(false); @@ -487,9 +495,11 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); + assertTrue(dt.isNotYetDefined()); assertTrue(dt.isZeroLength()); assertTrue(innerUnion.isEquivalent(model.viewComposite)); + assertTrue(innerUnion.isNotYetDefined()); assertTrue(innerUnion.isZeroLength()); assertIsPackingEnabled(false); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataReferencesTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataReferencesTest.java index be03914c01..0c7eee4789 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataReferencesTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/DataReferencesTest.java @@ -190,7 +190,8 @@ public class DataReferencesTest extends AbstractGhidraHeadedIntegrationTest { int e = i / 24; int f = i % 24; assertEquals( - "dword ptr [ArrayA[" + e + "].field_0x" + Integer.toHexString(f) + "]", + "dword ptr [ArrayA[" + e + "].field" + f + "_0x" + Integer.toHexString(f) + + "]", opStr); } } @@ -233,10 +234,13 @@ public class DataReferencesTest extends AbstractGhidraHeadedIntegrationTest { assertEquals("dword ptr [StructA]", opStr); } else { - int f = (i / 24) * 24; + int fOrdinal = i / 24; + int fOffset = fOrdinal * 24; int e = (i % 24) / 4; assertEquals( - "dword ptr [StructA.field_0x" + Integer.toHexString(f) + "[" + e + "]]", + "dword ptr [StructA.field" + fOrdinal + "_0x" + + Integer.toHexString(fOffset) + "[" + e + + "]]", opStr); } } @@ -327,10 +331,12 @@ public class DataReferencesTest extends AbstractGhidraHeadedIntegrationTest { assertEquals("dword ptr [StructA]", opStr); } else { - int f1 = (i / 24) * 24; + int f1Ordinal = i / 24; + int f1Offset = f1Ordinal * 24; int f2 = i % 24; - assertEquals("dword ptr [StructA.field_0x" + Integer.toHexString(f1) + - ".field_0x" + Integer.toHexString(f2) + "]", opStr); + assertEquals("dword ptr [StructA.field" + f1Ordinal + "_0x" + + Integer.toHexString(f1Offset) + + ".field" + f2 + "_0x" + Integer.toHexString(f2) + "]", opStr); } } } 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 c6b8e8d800..8a5c097ef9 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 @@ -354,8 +354,8 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { DataType structdt2 = dataMgr.getDataType(rootCP, "mystruct.conflict"); assertEquals(100, structdt.getLength()); - assertEquals("f1", structdt.getComponentAt(0).getFieldName()); - assertEquals("f2", structdt.getComponentAt(10).getFieldName()); + assertEquals("f1", structdt.getComponentContaining(0).getFieldName()); + assertEquals("f2", structdt.getComponentContaining(10).getFieldName()); assertNull(structdt2); } @@ -378,7 +378,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { Structure struct2dt = (Structure) dataMgr.getDataType(rootCP, "mystruct2"); assertEquals(100, structdt.getLength()); - assertEquals("ptr_to_struct1", struct2dt.getComponentAt(0).getFieldName()); + assertEquals("ptr_to_struct1", struct2dt.getComponentContaining(0).getFieldName()); } @Test @@ -699,7 +699,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - DataTypeComponent dtc = structdt.getComponentAt(14); + DataTypeComponent dtc = structdt.getComponentContaining(14); DataType anonDT = dtc.getDataType(); assertEquals("anon_struct_for_f3_f4", anonDT.getName()); } @@ -717,8 +717,13 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - assertNotNull(structdt.getFlexibleArrayComponent()); - + DataTypeComponent component = structdt.getComponent(structdt.getNumComponents() - 1); + assertNotNull(component); + assertEquals(0, component.getLength()); + DataType dt = component.getDataType(); + assertTrue(dt instanceof Array); + Array a = (Array) dt; + assertEquals(0, a.getNumElements()); } /* @@ -739,7 +744,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - assertNotNull(structdt.getFlexibleArrayComponent()); + assertHasFlexArray(structdt); } @@ -761,7 +766,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - assertNotNull(structdt.getFlexibleArrayComponent()); + assertHasFlexArray(structdt); } @Test @@ -778,9 +783,73 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - assertTrue(structdt.getDescription().contains("Missing member flexarray")); - assertNull(structdt.getFlexibleArrayComponent()); + DataTypeComponent component = structdt.getComponentContaining(99); + assertNotNull(component); + assertEquals(0, component.getLength()); + DataType dt = component.getDataType(); + assertTrue(dt.isEquivalent(new ArrayDataType(IntegerDataType.dataType, 0, -1))); + + assertEquals(100, structdt.getLength()); + + } + + @Test + public void testStructZeroLenField() + throws CancelledException, IOException, DWARFException { + + DebugInfoEntry intDIE = addInt(cu); + + DebugInfoEntry emptyStructDIE = newStruct("emptystruct", 0).create(cu); + DebugInfoEntry structDIE = newStruct("mystruct", 10).create(cu); + newMember(structDIE, "f1", intDIE, 0).create(cu); + newMember(structDIE, "f2", emptyStructDIE, 4).create(cu); + + importAllDataTypes(); + + Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); + + assertEquals(2, structdt.getNumDefinedComponents()); + DataTypeComponent f1 = structdt.getDefinedComponents()[0]; + assertEquals(0, f1.getOffset()); + assertEquals(4, f1.getLength()); + DataTypeComponent f2 = structdt.getDefinedComponents()[1]; + assertEquals(4, f2.getOffset()); + assertTrue(f2.getDataType().isZeroLength()); + assertEquals(10, structdt.getLength()); + } + + @Test + public void testStruct2ZeroLenField() + throws CancelledException, IOException, DWARFException { + + DebugInfoEntry intDIE = addInt(cu); + + DebugInfoEntry emptyStructDIE = newStruct("emptystruct", 0).create(cu); + DebugInfoEntry structDIE = newStruct("mystruct", 10).create(cu); + newMember(structDIE, "f1", intDIE, 0).create(cu); + newMember(structDIE, "f2", emptyStructDIE, 4).create(cu); + newMember(structDIE, "f3", emptyStructDIE, 4).create(cu); + newMember(structDIE, "f4", intDIE, 4).create(cu); + + importAllDataTypes(); + + Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); + assertEquals(4, structdt.getNumDefinedComponents()); + DataTypeComponent f1 = structdt.getDefinedComponents()[0]; + assertEquals(0, f1.getOffset()); + assertEquals(4, f1.getLength()); + DataTypeComponent f2 = structdt.getDefinedComponents()[1]; + assertEquals(4, f2.getOffset()); + assertTrue(f2.getDataType().isZeroLength()); + DataTypeComponent f3 = structdt.getDefinedComponents()[2]; + assertEquals(4, f3.getOffset()); + assertTrue(f3.getDataType().isZeroLength()); + DataTypeComponent f4 = structdt.getDefinedComponents()[3]; + assertEquals(4, f4.getOffset()); + assertEquals(4, f4.getLength()); + + assertEquals(10, structdt.getLength()); } @Test @@ -862,8 +931,6 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { @Test public void testUnionFlexArray() throws CancelledException, IOException, DWARFException { - // flex array in a union is converted to an 1 element array (if it can fit) - DebugInfoEntry intDIE = addInt(cu); DebugInfoEntry arrayDIE = newArray(cu, intDIE, false, -1); @@ -920,10 +987,10 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase { importAllDataTypes(); Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct"); - DataTypeComponent f1dtc = structdt.getComponentAt(0); - DataTypeComponent f2dtc = structdt.getComponentAt(10); - DataTypeComponent f3dtc = structdt.getComponentAt(20); - DataTypeComponent f4dtc = structdt.getComponentAt(40); + DataTypeComponent f1dtc = structdt.getComponentContaining(0); + DataTypeComponent f2dtc = structdt.getComponentContaining(10); + DataTypeComponent f3dtc = structdt.getComponentContaining(20); + DataTypeComponent f4dtc = structdt.getComponentContaining(40); assertEquals(f1dtc.getDataType(), f2dtc.getDataType()); assertEquals(f1dtc.getDataType(), f3dtc.getDataType()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/ObjectCacheTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/ObjectCacheTest.java index 3508f7600c..2da7410d93 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/ObjectCacheTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/ObjectCacheTest.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database; import static org.junit.Assert.*; @@ -25,8 +22,6 @@ import java.util.ConcurrentModificationException; import org.junit.Test; -import ghidra.program.database.DBObjectCache; -import ghidra.program.database.DatabaseObject; import ghidra.program.model.address.KeyRange; import ghidra.test.AbstractGhidraHeadedIntegrationTest; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java index 2934ce2138..9a9588a25b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java @@ -86,8 +86,7 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT struct.add(new ByteDataType(), "field0", "Comment1"); struct.add(new WordDataType(), null, "Comment2"); struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); -// struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", "Flex Comment"); + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "flex", "Flex Comment"); return struct; } @@ -117,7 +116,7 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT public void testStructureBitFields() throws Exception { openStructure(addr(0x1010)); assertMnemonic("Test", addr(0x1010), 0); - assertComponents(); + assertComponents("Test"); } @Test @@ -130,10 +129,10 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT openStructure(addr(0x1010)); assertMnemonic("BitfieldDType", addr(0x1010), 0); - assertComponents(); + assertComponents("BitfieldDType"); } - private void assertComponents() { + private void assertComponents(String typeName) { assertMnemonic("int:3", addr(0x1010), 1); assertOperand("0h", addr(0x1010), 1); assertMnemonic("int:24", addr(0x1010), 2); @@ -152,6 +151,10 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT assertOperand("EFh", addr(0x1017), 0); assertMnemonic("dw", addr(0x1018), 0); assertOperand("1234h", addr(0x1018), 0); + assertMnemonic("ddw", addr(0x101c), 0); + assertOperand("89ABCDEFh", addr(0x101c), 0); + // flex-array should appear as pre-comment for next code unit + assertPreComment("Zero-length Component: char[0] " + typeName + ".flex", addr(0x1020), 0); } private void assertMnemonic(String expectedValue, Address addr, int occurrence) { @@ -164,6 +167,11 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT assertEquals(expectedValue, plugin.getCurrentFieldText()); } + private void assertPreComment(String expectedValue, Address addr, int occurrence) { + plugin.goToField(addr, "Pre-Comment", occurrence, 0, 0); + assertEquals(expectedValue, plugin.getCurrentFieldText()); + } + private void openStructure(Address address) { // open the structure plugin.goToField(address, "+", 0, 0); @@ -209,14 +217,7 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT protected DataTypeComponent[] getAllComponents(MemBuffer buf) { try { Structure struct = createStructure(dataMgr); - DataTypeComponent[] components = struct.getComponents(); - if (struct.hasFlexibleArrayComponent()) { - DataTypeComponent[] newArray = new DataTypeComponent[components.length + 1]; - System.arraycopy(components, 0, newArray, 0, components.length); - newArray[components.length] = struct.getFlexibleArrayComponent(); - components = newArray; - } - return components; + return struct.getComponents(); } catch (InvalidDataTypeException e) { return null; // test should fail as a result diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java index 3a3c02f0ff..cdde2d08b7 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java @@ -128,6 +128,8 @@ public class DataTypeParserTest extends AbstractEditorTest { checkValidDt("byte*64*32**16*8"); checkValidDt("byte*8*"); checkValidDt("byte*32*16*32"); + checkValidDt("byte[]"); // treated the same as byte[0] + checkValidDt("byte[0]"); checkValidDt("byte[5]"); checkValidDt("byte[22][13]"); checkValidDt("byte*[2]"); @@ -141,8 +143,6 @@ public class DataTypeParserTest extends AbstractEditorTest { checkInvalidDt("aaa*{"); checkInvalidDt("byte*5"); checkInvalidDt("byte*16*[.]"); - checkInvalidDt("byte[]"); - checkInvalidDt("byte[0]"); checkInvalidDt("*byte"); checkInvalidDt("byte[7]*[12a]"); checkInvalidDt("*"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/CodeUnitTableCellDataTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/CodeUnitTableCellDataTest.java index 60197d2e19..30b6489d1b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/CodeUnitTableCellDataTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/CodeUnitTableCellDataTest.java @@ -15,8 +15,7 @@ */ package ghidra.util.table; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; @@ -209,7 +208,7 @@ public class CodeUnitTableCellDataTest extends AbstractProgramBasedTest { String preview = data.getDisplayString(); // this is the mnemonic and operand inside of the structure at field [2] - assertEquals("ds \"\" (TestStruct.field_0x5)", preview); + assertEquals("ds \"\" (TestStruct.field2_0x5)", preview); } @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/PreviewTableCellDataTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/PreviewTableCellDataTest.java index cf3b28ed97..16fd306d5c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/PreviewTableCellDataTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/table/PreviewTableCellDataTest.java @@ -15,8 +15,7 @@ */ package ghidra.util.table; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; @@ -26,27 +25,9 @@ import ghidra.app.util.viewer.field.ArrayElementFieldLocation; import ghidra.app.util.viewer.field.BrowserCodeUnitFormat; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.CharDataType; -import ghidra.program.model.data.Integer16DataType; -import ghidra.program.model.data.StringDataType; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.data.TerminatedStringDataType; -import ghidra.program.model.data.UnicodeDataType; -import ghidra.program.model.listing.CodeUnitFormat; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.Program; -import ghidra.program.model.listing.Variable; -import ghidra.program.util.AddressFieldLocation; -import ghidra.program.util.BytesFieldLocation; -import ghidra.program.util.FunctionNameFieldLocation; -import ghidra.program.util.LabelFieldLocation; -import ghidra.program.util.MnemonicFieldLocation; -import ghidra.program.util.OperandFieldLocation; -import ghidra.program.util.ProgramLocation; -import ghidra.program.util.RepeatableCommentFieldLocation; -import ghidra.program.util.VariableNameFieldLocation; -import ghidra.program.util.XRefFieldLocation; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.util.*; import ghidra.test.AbstractProgramBasedTest; import ghidra.test.ClassicSampleX86ProgramBuilder; @@ -204,7 +185,7 @@ public class PreviewTableCellDataTest extends AbstractProgramBasedTest { String preview = data.getDisplayString(); // this is the mnemonic and operand inside of the structure at field [2] - assertEquals("ds \"\" (TestStruct.field_0x5)", preview); + assertEquals("ds \"\" (TestStruct.field2_0x5)", preview); } @Test diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java index 34ef16cda9..4c9b1c011a 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java @@ -36,8 +36,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.data.*; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -356,4 +355,22 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { protected Address addr(long l) { return space.getAddress(l); } + + protected void assertHasFlexArray(Structure struct) { + DataTypeComponent component = struct.getComponent(struct.getNumComponents() - 1); + assertNotNull(component); + assertEquals(0, component.getLength()); + DataType dt = component.getDataType(); + assertTrue(dt instanceof Array); + Array a = (Array) dt; + assertEquals(0, a.getNumElements()); + } + + protected void assertMissingFlexArray(Structure struct) { + DataTypeComponent component = struct.getComponent(struct.getNumComponents() - 1); + if (component == null) { + return; + } + assertNotEquals(0, component.getLength()); + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCStructureDBBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCStructureDBBitFieldTest.java deleted file mode 100644 index fe47ec47c8..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCStructureDBBitFieldTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * - */ -package ghidra.program.database.data; - -import ghidra.program.model.data.*; - -public class MSVCStructureDBBitFieldTest extends MSVCStructureImplBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - getDataTypeManager().startTransaction("Test"); - super.setUp(); - } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCStructureDBBitFieldTest.class) { - if (dataMgr == null) { - dataMgr = new StandAloneDataTypeManager("Test"); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); - DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); - } - return dataMgr; - } - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCUnionDBBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCUnionDBBitFieldTest.java deleted file mode 100644 index 9bd6151b54..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/MSVCUnionDBBitFieldTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * - */ -package ghidra.program.database.data; - -import ghidra.program.model.data.*; - -public class MSVCUnionDBBitFieldTest extends MSVCUnionImplBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - getDataTypeManager().startTransaction("Test"); - super.setUp(); - } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCUnionDBBitFieldTest.class) { - if (dataMgr == null) { - dataMgr = new StandAloneDataTypeManager("Test"); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); - DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); - } - return dataMgr; - } - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBBigEndianBitFieldTest.java deleted file mode 100644 index 1e168359a4..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBBigEndianBitFieldTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * - */ -package ghidra.program.database.data; - -import ghidra.program.model.data.*; - -public class StructureDBBigEndianBitFieldTest extends StructureImplBigEndianBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - getDataTypeManager().startTransaction("Test"); - super.setUp(); - } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCStructureDBBitFieldTest.class) { - if (dataMgr == null) { - dataMgr = new StandAloneDataTypeManager("Test"); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); - DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); - } - return dataMgr; - } - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBLittleEndianBitFieldTest.java deleted file mode 100644 index 3ad2534133..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDBLittleEndianBitFieldTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * - */ -package ghidra.program.database.data; - -import ghidra.program.model.data.*; - -public class StructureDBLittleEndianBitFieldTest extends StructureImplLittleEndianBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - getDataTypeManager().startTransaction("Test"); - super.setUp(); - } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCStructureDBBitFieldTest.class) { - if (dataMgr == null) { - dataMgr = new StandAloneDataTypeManager("Test"); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); - DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); - } - return dataMgr; - } - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBBigEndianBitFieldTest.java deleted file mode 100644 index 935de83bd1..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBBigEndianBitFieldTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * - */ -package ghidra.program.database.data; - -import ghidra.program.model.data.*; - -public class UnionDBBigEndianBitFieldTest extends UnionImplBigEndianBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - getDataTypeManager().startTransaction("Test"); - super.setUp(); - } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCStructureDBBitFieldTest.class) { - if (dataMgr == null) { - dataMgr = new StandAloneDataTypeManager("Test"); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); - DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); - } - return dataMgr; - } - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBLittleEndianBitFieldTest.java deleted file mode 100644 index 6df4d1a224..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDBLittleEndianBitFieldTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * - */ -package ghidra.program.database.data; - -import ghidra.program.model.data.*; - -public class UnionDBLittleEndianBitFieldTest extends UnionImplLittleEndianBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - getDataTypeManager().startTransaction("Test"); - super.setUp(); - } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCStructureDBBitFieldTest.class) { - if (dataMgr == null) { - dataMgr = new StandAloneDataTypeManager("Test"); - DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); - DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); - } - return dataMgr; - } - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeBitFieldTest.java new file mode 100644 index 0000000000..445d165506 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeBitFieldTest.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import org.junit.Before; + +/** + * This test uses the CParser to parse all datatypes defined by bitfields.h. + * The parse process entails building up the composite types using the various + * "Impl" datatypes followed by their being resolved into the associated datatype + * manager where DB datatypes will be established. + *
+ * When fetching datatypes from the datatype manager they will naturally be + * returned as DB datatypes when applicable (see {@link #getStructure(String)} and + * {@link #getUnion(String)}). If the test class name contains "Impl" a deep copy + * will be performed on datatypes to return as an non-DB Impl datatype. + */ +public abstract class AbstractCompositeBitFieldTest extends AbstractCompositeTest { + + protected static final String C_SOURCE_FILE = "ghidra/app/util/cparser/bitfields.h"; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + parseCHeaderFile(C_SOURCE_FILE); + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java deleted file mode 100644 index ee17f1ce98..0000000000 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.model.data; - -import static org.junit.Assert.*; - -import java.io.FileNotFoundException; -import java.io.InputStream; - -import org.junit.Before; - -import generic.test.AbstractGTest; -import ghidra.app.util.cparser.C.CParser; -import ghidra.util.Msg; -import resources.ResourceManager; - -public abstract class AbstractCompositeImplBitFieldTest extends AbstractGTest { - - protected static final String C_SOURCE_FILE = "ghidra/app/util/cparser/bitfields.h"; - - @Before - public void setUp() throws Exception { - - DataTypeManager dataMgr = getDataTypeManager(); - if (dataMgr.getDataTypeCount(false) != 0) { -// Msg.info(this, "Using previously parsed data types"); - return; // already have types - } - - Msg.info(this, "Parsing data types from " + C_SOURCE_FILE); - - CParser parser = new CParser(dataMgr, true, null); - - try (InputStream is = ResourceManager.getResourceAsStream(C_SOURCE_FILE)) { - if (is == null) { - throw new FileNotFoundException("Resource not found: " + C_SOURCE_FILE); - } - Msg.debug(this, "Parsing C headers from " + C_SOURCE_FILE); - parser.parse(is); - } - } - - protected class MyDataTypeManager extends StandAloneDataTypeManager { - MyDataTypeManager(String name, DataOrganization dataOrg) { - super(name); - this.dataOrganization = dataOrg; - } - } - - abstract DataTypeManager getDataTypeManager(); - - Structure getStructure(String name) { - DataType dataType = getDataTypeManager().getDataType("/" + name); - assertTrue("Data type not found: " + name, dataType instanceof Structure); - return (Structure) dataType; - } - - Union getUnion(String name) { - DataType dataType = getDataTypeManager().getDataType("/" + name); - assertTrue("Data type not found: " + name, dataType instanceof Union); - return (Union) dataType; - } - -} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java new file mode 100644 index 0000000000..a37de54e34 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java @@ -0,0 +1,211 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import static org.junit.Assert.*; + +import java.io.*; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; + +import generic.test.AbstractGTest; +import ghidra.app.util.cparser.C.CParser; +import ghidra.app.util.cparser.C.ParseException; +import ghidra.util.Msg; +import resources.ResourceManager; + +abstract class AbstractCompositeTest extends AbstractGTest { + + private HashMap copyMap = new HashMap<>(); + + private final boolean returnDeepCopyOnQuery; + + protected AbstractCompositeTest() { + // If test class name contains "Impl" enable deep copy on query + returnDeepCopyOnQuery = getClass().getSimpleName().contains("Impl"); + } + + public void setUp() throws Exception { + copyMap.clear(); + } + + protected void parseCHeaderFile(String headerResourcePath) throws IOException, ParseException { + + DataTypeManager dataMgr = getDataTypeManager(); + if (dataMgr.getDataTypeCount(false) != 0) { +// Msg.info(this, "Using previously parsed data types"); + return; // already have types + } + + Msg.info(this, "Parsing data types from " + headerResourcePath); + + CParser parser = new CParser(dataMgr, true, null); + + try (InputStream is = ResourceManager.getResourceAsStream(headerResourcePath)) { + if (is == null) { + throw new FileNotFoundException("Resource not found: " + headerResourcePath); + } + // Msg.debug(this, "Parsing C headers from " + headerResourcePath); + parser.parse(is); + } + + // uncomment to generate datatype archive + writeArchive(headerResourcePath); + } + + private void writeArchive(String headerResourcePath) throws IOException { + URL resource = ResourceManager.getResource(headerResourcePath); + File f = new File(resource.getPath() + ".gdt"); + if (f.exists()) { + f.delete(); + } + + FileDataTypeManager fileDtMgr = FileDataTypeManager.createFileArchive(f); + int txId = fileDtMgr.startTransaction("Save Datatypes"); + try { + Iterator composites = getDataTypeManager().getAllComposites(); + while (composites.hasNext()) { + fileDtMgr.addDataType(composites.next(), null); + } + } + finally { + fileDtMgr.endTransaction(txId, true); + } + + fileDtMgr.save(); + fileDtMgr.close(); + + Msg.debug(this, "Saved datatype archive: " + f.getAbsolutePath()); + } + + protected final DataTypeManager createDataTypeManager(String name, DataOrganization dataOrg) { + return new StandAloneDataTypeManager(name, dataOrg); + } + + abstract DataTypeManager getDataTypeManager(); + + protected Structure getStructure(String name) { + DataTypeManager dtMgr = getDataTypeManager(); + DataType dataType = dtMgr.getDataType("/" + name); + assertTrue("Data type not found: " + name, dataType instanceof Structure); + if (returnDeepCopyOnQuery) { + dataType = deepCopy(dataType); // TODO: need deep copy + } + return (Structure) dataType; + } + + protected Union getUnion(String name) { + DataTypeManager dtMgr = getDataTypeManager(); + DataType dataType = dtMgr.getDataType("/" + name); + assertTrue("Data type not found: " + name, dataType instanceof Union); + if (returnDeepCopyOnQuery) { + dataType = deepCopy(dataType); + } + return (Union) dataType; + } + + private void copyCompositeSettings(Composite from, Composite to) { + to.setDescription(from.getDescription()); + if (from.hasExplicitPackingValue()) { + to.setExplicitPackingValue(from.getExplicitPackingValue()); + } + else if (from.hasDefaultPacking()) { + to.setToDefaultPacking(); + } + if (from.hasExplicitMinimumAlignment()) { + to.setExplicitMinimumAlignment(from.getExplicitMinimumAlignment()); + } + else if (from.isMachineAligned()) { + to.setToMachineAligned(); + } + } + + private DataType deepCopy(DataType dt) { + + DataTypeManager dtMgr = dt.getDataTypeManager(); + long id = dtMgr.getID(dt); + if (id > 0 && copyMap.containsKey(id)) { + return copyMap.get(id); + } + + if (dt instanceof Structure) { + Structure s = (Structure) dt; + StructureDataType struct = + new StructureDataType(s.getCategoryPath(), s.getName(), s.getLength(), dtMgr); + copyMap.put(id, struct); + copyCompositeSettings(s, struct); + for (DataTypeComponent dtc : s.getDefinedComponents()) { + if (struct.isPackingEnabled()) { + struct.add(deepCopy(dtc.getDataType()), dtc.getLength(), dtc.getFieldName(), + dtc.getComment()); + } + else if (dtc.isBitFieldComponent()) { + BitFieldDataType bf = (BitFieldDataType) dtc.getDataType(); + try { + struct.insertBitFieldAt(dtc.getOffset(), dtc.getLength(), bf.getBitOffset(), + bf.getBaseDataType().copy(dtMgr), bf.getDeclaredBitSize(), + dtc.getFieldName(), dtc.getComment()); + } + catch (InvalidDataTypeException e) { + failWithException("Unexpected exception", e); + } + } + else { + struct.insertAtOffset(dtc.getOffset(), deepCopy(dtc.getDataType()), + dtc.getLength(), dtc.getFieldName(), dtc.getComment()); + } + } + return struct; + } + else if (dt instanceof Union) { + Union u = (Union) dt; + UnionDataType union = new UnionDataType(u.getCategoryPath(), u.getName(), dtMgr); + copyMap.put(id, union); + copyCompositeSettings(u, union); + for (DataTypeComponent dtc : u.getDefinedComponents()) { + union.add(deepCopy(dtc.getDataType()), dtc.getLength(), dtc.getFieldName(), + dtc.getComment()); + } + return union; + } + else if (dt instanceof Array) { + Array a = (Array) dt; + a = new ArrayDataType(deepCopy(a.getDataType()), a.getNumElements(), + a.getElementLength(), dtMgr); + copyMap.put(id, a); + return a; + } + else if (dt instanceof Pointer) { + Pointer p = (Pointer) dt; + p = new PointerDataType(deepCopy(p.getDataType()), + p.hasLanguageDependantLength() ? -1 : p.getLength(), dtMgr); + copyMap.put(id, p); + return p; + } + else if (dt instanceof TypeDef) { + TypeDef t = (TypeDef) dt; + t = new TypedefDataType(t.getCategoryPath(), t.getName(), deepCopy(t.getDataType()), + dtMgr); + copyMap.put(id, t); + return t; + } + dt = dt.copy(dtMgr); + copyMap.put(id, dt); + return dt; + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeZeroArrayTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeZeroArrayTest.java new file mode 100644 index 0000000000..0c9a85a41d --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeZeroArrayTest.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import org.junit.Before; + +/** + * This test uses the CParser to parse all datatypes defined by zeroarray.h. + * The parse process entails building up the composite types using the various + * "Impl" datatypes followed by their being resolved into the associated datatype + * manager where DB datatypes will be established. + *
+ * When fetching datatypes from the datatype manager they will naturally be + * returned as DB datatypes when applicable (see {@link #getStructure(String)} and + * {@link #getUnion(String)}). If the test class name contains "Impl" a deep copy + * will be performed on datatypes to return as an non-DB Impl datatype. + */ +public abstract class AbstractCompositeZeroArrayTest extends AbstractCompositeTest { + + protected static final String C_SOURCE_FILE = "ghidra/app/util/cparser/zeroarray.h"; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + parseCHeaderFile(C_SOURCE_FILE); + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/CompositeImplZeroArrayTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/CompositeImplZeroArrayTest.java new file mode 100644 index 0000000000..8fb07c208d --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/CompositeImplZeroArrayTest.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +public class CompositeImplZeroArrayTest extends CompositeZeroArrayTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/CompositeZeroArrayTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/CompositeZeroArrayTest.java new file mode 100644 index 0000000000..9b5b232dc4 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/CompositeZeroArrayTest.java @@ -0,0 +1,131 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import org.junit.Test; + +public class CompositeZeroArrayTest extends AbstractCompositeZeroArrayTest { + + // NOTE: In the absence of bit-fields these tests are not sensitive to endianess + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (CompositeZeroArrayTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureZeroArrayS1() { + Structure struct = getStructure("zeroArrayStruct1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/zeroArrayStruct1\n" + + "pack()\n" + + "Structure zeroArrayStruct1 {\n" + + " 0 int 4 a \"\"\n" + + " 4 char[2] 2 a1 \"\"\n" + + " 6 char[0] 0 x \"\"\n" + + " 8 int[0] 0 y \"\"\n" + + " 8 int 4 b \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureZeroArrayS2() { + Structure struct = getStructure("zeroArrayStruct2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/zeroArrayStruct2\n" + + "pack()\n" + + "Structure zeroArrayStruct2 {\n" + + " 0 int 4 a \"\"\n" + + " 4 char[2] 2 a1 \"\"\n" + + " 6 char[0] 0 x \"\"\n" + + " 8 int[0] 0 y \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureZeroArrayS3() { + Structure struct = getStructure("zeroArrayStruct3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/zeroArrayStruct3\n" + + "pack()\n" + + "Structure zeroArrayStruct3 {\n" + + " 0 int 4 a \"\"\n" + + " 4 zeroArrayStruct2 8 s \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureZeroArrayU1() { + Union union = getUnion("zeroArrayUnion1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/zeroArrayUnion1\n" + + "pack()\n" + + "Union zeroArrayUnion1 {\n" + + " 0 int 4 a \"\"\n" + + " 0 char[2] 2 a1 \"\"\n" + + " 0 char[0] 0 x \"\"\n" + + " 0 int[0] 0 y \"\"\n" + + " 0 int 4 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", union); + //@formatter:on + } + + @Test + public void testStructureZeroArrayU2() { + Union union = getUnion("zeroArrayUnion2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/zeroArrayUnion2\n" + + "pack()\n" + + "Union zeroArrayUnion2 {\n" + + " 0 int 4 a \"\"\n" + + " 0 char[2] 2 a1 \"\"\n" + + " 0 char[0] 0 x \"\"\n" + + " 0 int[0] 0 y \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", union); + //@formatter:on + } + + @Test + public void testStructureZeroArrayU3() { + Union union = getUnion("zeroArrayUnion3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/zeroArrayUnion3\n" + + "pack()\n" + + "Union zeroArrayUnion3 {\n" + + " 0 int 4 a \"\"\n" + + " 0 zeroArrayStruct2 8 s \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", union); + //@formatter:on + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureBitFieldTest.java new file mode 100644 index 0000000000..b9efb159c8 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureBitFieldTest.java @@ -0,0 +1,829 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class MSVCStructureBitFieldTest extends AbstractCompositeBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCStructureBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureBitFieldsA1() { + Structure struct = getStructure("A1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + + "pack()\n" + + "Structure A1 {\n" + + " 0 char[5] 5 a \"\"\n" + + " 8 int:3(0) 1 b \"\"\n" + + " 8 int:8(3) 2 c \"\"\n" + + " 9 int:8(3) 2 d \"\"\n" + + " 10 int:6(3) 2 e \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsA2() { + Structure struct = getStructure("A2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + + "pack()\n" + + "Structure A2 {\n" + + " 0 oddStruct 5 a \"\"\n" + + " 8 int:3(0) 1 b \"\"\n" + + " 8 int:8(3) 2 c \"\"\n" + + " 9 int:8(3) 2 d \"\"\n" + + " 10 int:6(3) 2 e \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsA3() { + Structure struct = getStructure("A3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + + "pack()\n" + + "Structure A3 {\n" + + " 0 char[5] 5 a \"\"\n" + + " 8 int:3(0) 1 b \"\"\n" + + " 8 int:8(3) 2 c \"\"\n" + + " 12 int:85(0) 4 d \"\"\n" + + " 16 int:6(0) 1 e \"\"\n" + + "}\n" + + "Size = 20 Actual Alignment = 4", struct); + //@formatter:on + DataTypeComponent c = struct.getComponent(3); + assertTrue(c.isBitFieldComponent()); + BitFieldDataType bfDt = (BitFieldDataType) c.getDataType(); + assertEquals(4, bfDt.getBaseTypeSize()); + assertEquals(32, bfDt.getBitSize()); + assertEquals(85, bfDt.getDeclaredBitSize()); + } + + @Test + public void testStructureBitFieldsB1() { + Structure struct = getStructure("B1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + + "pack()\n" + + "Structure B1 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1Flex() { + Structure struct = getStructure("B1flex"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + + "pack()\n" + + "Structure B1flex {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + " 12 long[0] 0 flex \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2() { + Structure struct = getStructure("B2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + + "pack()\n" + + "Structure B2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3() { + Structure struct = getStructure("B3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + + "pack()\n" + + "Structure B3 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1() { + Structure struct = getStructure("Z1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + + "pack()\n" + + "Structure Z1 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:0(0) 0 \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2() { + Structure struct = getStructure("Z2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + + "pack()\n" + + "Structure Z2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 int:0(0) 0 \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3() { + Structure struct = getStructure("Z3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + + "pack()\n" + + "Structure Z3 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4() { + Structure struct = getStructure("Z4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + + "pack()\n" + + "Structure Z4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5() { + Structure struct = getStructure("Z5"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + + "pack()\n" + + "Structure Z5 {\n" + + " 0 char 1 a \"\"\n" + + " 8 int:0(0) 0 \"\"\n" + + " 8 longlong:6(0) 1 b \"\"\n" + + " 16 int:8(0) 1 c \"\"\n" + + " 20 char 1 d \"\"\n" + + "}\n" + + "Size = 24 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ6() { + Structure struct = getStructure("Z6"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + + "pack()\n" + + "Structure Z6 {\n" + + " 0 char 1 a \"\"\n" + + " 8 int:0(0) 0 \"\"\n" + + " 8 longlong:6(0) 1 b \"\"\n" + + " 16 int:8(0) 1 c \"\"\n" + + " 20 char 1 d \"\"\n" + + " 24 longlong:6(0) 1 e \"\"\n" + + " 32 int:8(0) 1 f \"\"\n" + + " 36 char 1 g \"\"\n" + + "}\n" + + "Size = 40 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p1() { + Structure struct = getStructure("B1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + + "pack(1)\n" + + "Structure B1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p1() { + Structure struct = getStructure("B2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + + "pack(1)\n" + + "Structure B2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 4 int:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p1() { + Structure struct = getStructure("B3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + + "pack(1)\n" + + "Structure B3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p1() { + Structure struct = getStructure("Z1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + + "pack(1)\n" + + "Structure Z1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:0(0) 0 \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p1() { + Structure struct = getStructure("Z2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + + "pack(1)\n" + + "Structure Z2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 int:0(0) 0 \"\"\n" + + " 7 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1() { + Structure struct = getStructure("Z3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + + "pack(1)\n" + + "Structure Z3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 4 int:4(0) 1 d \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1T() { + Structure struct = getStructure("Z3p1T"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + + "pack()\n" + + "Structure Z3p1T {\n" + + " 0 char 1 a \"\"\n" + + " 1 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p1() { + Structure struct = getStructure("Z4p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + + "pack(1)\n" + + "Structure Z4p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 3 int:8(0) 1 c \"\"\n" + + " 7 longlong:0(0) 0 \"\"\n" + + " 7 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p2() { + Structure struct = getStructure("B1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + + "pack(2)\n" + + "Structure B1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p2() { + Structure struct = getStructure("B2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + + "pack(2)\n" + + "Structure B2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p2() { + Structure struct = getStructure("B3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + + "pack(2)\n" + + "Structure B3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB4p2() { + Structure struct = getStructure("B4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + + "pack(2)\n" + + "Structure B4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong 8 d \"\"\n" + + " 16 int:4(0) 1 e \"\"\n" + + "}\n" + + "Size = 20 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2() { + Structure struct = getStructure("Z1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + + "pack(2)\n" + + "Structure Z1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:0(0) 0 \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2x() { + Structure struct = getStructure("Z1p2x"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + + "pack(2)\n" + + "Structure Z1p2x {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:0(0) 0 \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + " 8 short:4(4) 1 d1 \"\"\n" + + " 9 short:4(0) 1 d2 \"\"\n" + + " 9 short:4(4) 1 d3 \"\"\n" + + " 10 short:4(0) 1 d4 \"\"\n" + + " 10 short:4(4) 1 d5 \"\"\n" + + " 11 short:4(0) 1 d6 \"\"\n" + + " 11 short:4(4) 1 d7 \"\"\n" + + " 12 short:0(0) 0 \"\"\n" + + " 12 ushort:6(0) 1 _b \"\"\n" + + " 14 int:8(0) 1 _c \"\"\n" + + " 18 short:4(0) 1 _d \"\"\n" + + " 18 short:4(4) 1 _d1 \"\"\n" + + " 19 short:4(0) 1 _d2 \"\"\n" + + " 19 short:4(4) 1 _d3 \"\"\n" + + " 20 short:4(0) 1 _d4 \"\"\n" + + " 20 short:4(4) 1 _d5 \"\"\n" + + " 21 short:4(0) 1 _d6 \"\"\n" + + " 21 short:4(4) 1 _d7 \"\"\n" + + "}\n" + + "Size = 22 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p2() { + Structure struct = getStructure("Z2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + + "pack(2)\n" + + "Structure Z2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 int:0(0) 0 \"\"\n" + + " 8 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p2() { + Structure struct = getStructure("Z3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + + "pack(2)\n" + + "Structure Z3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 5 int:4(0) 1 d \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p2() { + Structure struct = getStructure("Z4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + + "pack(2)\n" + + "Structure Z4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p2() { + Structure struct = getStructure("Z5p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + + "pack(2)\n" + + "Structure Z5p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:12(0) 2 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p2() { + Structure struct = getStructure("x1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + + "pack(2)\n" + + "Structure x1p2 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p2() { + Structure struct = getStructure("x2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + + "pack(2)\n" + + "Structure x2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p2() { + Structure struct = getStructure("x3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + + "pack(2)\n" + + "Structure x3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(0) 0 \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p2() { + Structure struct = getStructure("x4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + + "pack(2)\n" + + "Structure x4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p4() { + Structure struct = getStructure("Z5p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + + "pack(4)\n" + + "Structure Z5p4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 ushort:12(0) 2 b \"\"\n" + + " 4 int:8(0) 1 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p4() { + Structure struct = getStructure("x1p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + + "pack(4)\n" + + "Structure x1p4 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p4() { + Structure struct = getStructure("x2p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + + "pack(4)\n" + + "Structure x2p4 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p4() { + Structure struct = getStructure("x3p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + + "pack(4)\n" + + "Structure x3p4 {\n" + + " 0 char 1 a \"\"\n" + + " 4 short:0(0) 0 \"\"\n" + + " 4 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p4() { + Structure struct = getStructure("x4p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + + "pack(4)\n" + + "Structure x4p4 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:27(0) 4 b \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT1() { + Structure struct = getStructure("T1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + + "pack()\n" + + "Structure T1 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 4 myEnum:3(0) 1 b \"\"\n" + + " 4 enumTypedef:3(3) 1 c \"\"\n" + + " 8 charTypedef:7(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT2() { + Structure struct = getStructure("T2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + + "pack()\n" + + "Structure T2 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 4 intTypedef:17(0) 3 b \"\"\n" + + " 6 enumTypedef:3(1) 1 c \"\"\n" + + " 8 charTypedef:3(0) 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1() { + Structure struct = getStructure("S1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + + "pack()\n" + + "Structure S1 {\n" + + " 0 B1 12 b1 \"\"\n" + + " 12 B2 8 b2 \"\"\n" + + " 20 Z1 12 z1 \"\"\n" + + " 32 Z2 12 z2 \"\"\n" + + " 48 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 56 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p1() { + Structure struct = getStructure("S1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + + "pack(1)\n" + + "Structure S1p1 {\n" + + " 0 B1 12 b1 \"\"\n" + + " 12 B2 8 b2 \"\"\n" + + " 20 Z1 12 z1 \"\"\n" + + " 32 Z2 12 z2 \"\"\n" + + " 44 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 52 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p1() { + Structure struct = getStructure("S2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + + "pack(1)\n" + + "Structure S2p1 {\n" + + " 0 B1p1 9 b1p1 \"\"\n" + + " 9 B2p1 7 b2p1 \"\"\n" + + " 16 Z1p1 9 z1p1 \"\"\n" + + " 25 Z2p1 9 z2p1 \"\"\n" + + " 34 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 42 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p2() { + Structure struct = getStructure("S1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + + "pack(2)\n" + + "Structure S1p2 {\n" + + " 0 B1 12 b1 \"\"\n" + + " 12 B2 8 b2 \"\"\n" + + " 20 Z1 12 z1 \"\"\n" + + " 32 Z2 12 z2 \"\"\n" + + " 44 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 52 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p2() { + Structure struct = getStructure("S2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + + "pack(2)\n" + + "Structure S2p2 {\n" + + " 0 B1p2 10 b1p2 \"\"\n" + + " 10 B2p2 8 b2p2 \"\"\n" + + " 18 Z1p2 10 z1p2 \"\"\n" + + " 28 Z2p2 10 z2p2 \"\"\n" + + " 38 Z3p2 8 z3p2 \"\"\n" + + "}\n" + + "Size = 46 Actual Alignment = 2", struct); + //@formatter:on + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java index 399932725b..7d5ac94722 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java @@ -15,815 +15,6 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.*; - -import org.junit.Test; - -public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCStructureImplBitFieldTest.class) { - if (dataMgr == null) { - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); - dataMgr = new MyDataTypeManager("test", dataOrg); - } - return dataMgr; - } - } - - @Test - public void testStructureBitFieldsA1() { - Structure struct = getStructure("A1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + - "pack()\n" + - "Structure A1 {\n" + - " 0 char[5] 5 a \"\"\n" + - " 8 int:3(0) 1 b \"\"\n" + - " 8 int:8(3) 2 c \"\"\n" + - " 9 int:8(3) 2 d \"\"\n" + - " 10 int:6(3) 2 e \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsA2() { - Structure struct = getStructure("A2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + - "pack()\n" + - "Structure A2 {\n" + - " 0 oddStruct 5 a \"\"\n" + - " 8 int:3(0) 1 b \"\"\n" + - " 8 int:8(3) 2 c \"\"\n" + - " 9 int:8(3) 2 d \"\"\n" + - " 10 int:6(3) 2 e \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsA3() { - Structure struct = getStructure("A3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + - "pack()\n" + - "Structure A3 {\n" + - " 0 char[5] 5 a \"\"\n" + - " 8 int:3(0) 1 b \"\"\n" + - " 8 int:8(3) 2 c \"\"\n" + - " 12 int:85(0) 4 d \"\"\n" + - " 16 int:6(0) 1 e \"\"\n" + - "}\n" + - "Size = 20 Actual Alignment = 4", struct); - //@formatter:on - DataTypeComponent c = struct.getComponent(3); - assertTrue(c.isBitFieldComponent()); - BitFieldDataType bfDt = (BitFieldDataType) c.getDataType(); - assertEquals(4, bfDt.getBaseTypeSize()); - assertEquals(32, bfDt.getBitSize()); - assertEquals(85, bfDt.getDeclaredBitSize()); - } - - @Test - public void testStructureBitFieldsB1() { - Structure struct = getStructure("B1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + - "pack()\n" + - "Structure B1 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1Flex() { - Structure struct = getStructure("B1flex"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + - "pack()\n" + - "Structure B1flex {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - " long[0] 0 flex \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2() { - Structure struct = getStructure("B2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + - "pack()\n" + - "Structure B2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 5 int:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3() { - Structure struct = getStructure("B3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + - "pack()\n" + - "Structure B3 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1() { - Structure struct = getStructure("Z1"); - - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + - "pack()\n" + - "Structure Z1 {\n" + - " 0 char 1 a \"\"\n" + - " 2 int:0(0) 1 \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2() { - Structure struct = getStructure("Z2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + - "pack()\n" + - "Structure Z2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 int:0(0) 1 \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3() { - Structure struct = getStructure("Z3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + - "pack()\n" + - "Structure Z3 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 5 int:4(0) 1 d \"\"\n" + - " 7 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4() { - Structure struct = getStructure("Z4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + - "pack()\n" + - "Structure Z4 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5() { - Structure struct = getStructure("Z5"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + - "pack()\n" + - "Structure Z5 {\n" + - " 0 char 1 a \"\"\n" + - " 8 int:0(0) 1 \"\"\n" + - " 8 longlong:6(0) 1 b \"\"\n" + - " 16 int:8(0) 1 c \"\"\n" + - " 20 char 1 d \"\"\n" + - "}\n" + - "Size = 24 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ6() { - Structure struct = getStructure("Z6"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + - "pack()\n" + - "Structure Z6 {\n" + - " 0 char 1 a \"\"\n" + - " 8 int:0(0) 1 \"\"\n" + - " 8 longlong:6(0) 1 b \"\"\n" + - " 16 int:8(0) 1 c \"\"\n" + - " 20 char 1 d \"\"\n" + - " 24 longlong:6(0) 1 e \"\"\n" + - " 32 int:8(0) 1 f \"\"\n" + - " 36 char 1 g \"\"\n" + - "}\n" + - "Size = 40 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1p1() { - Structure struct = getStructure("B1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + - "pack(1)\n" + - "Structure B1p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 7 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2p1() { - Structure struct = getStructure("B2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + - "pack(1)\n" + - "Structure B2p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 4 int:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 7 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3p1() { - Structure struct = getStructure("B3p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + - "pack(1)\n" + - "Structure B3p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 7 char 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p1() { - Structure struct = getStructure("Z1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + - "pack(1)\n" + - "Structure Z1p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:0(0) 1 \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 7 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2p1() { - Structure struct = getStructure("Z2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + - "pack(1)\n" + - "Structure Z2p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 7 int:0(0) 1 \"\"\n" + - " 7 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p1() { - Structure struct = getStructure("Z3p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + - "pack(1)\n" + - "Structure Z3p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 4 int:4(0) 1 d \"\"\n" + - " 6 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 7 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p1T() { - Structure struct = getStructure("Z3p1T"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + - "pack()\n" + - "Structure Z3p1T {\n" + - " 0 char 1 a \"\"\n" + - " 1 Z3p1 7 z3p1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4p1() { - Structure struct = getStructure("Z4p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + - "pack(1)\n" + - "Structure Z4p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 3 int:8(0) 1 c \"\"\n" + - " 7 longlong:0(0) 1 \"\"\n" + - " 7 char 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1p2() { - Structure struct = getStructure("B1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + - "pack(2)\n" + - "Structure B1p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2p2() { - Structure struct = getStructure("B2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + - "pack(2)\n" + - "Structure B2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 5 int:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3p2() { - Structure struct = getStructure("B3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + - "pack(2)\n" + - "Structure B3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB4p2() { - Structure struct = getStructure("B4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + - "pack(2)\n" + - "Structure B4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 longlong 8 d \"\"\n" + - " 16 int:4(0) 1 e \"\"\n" + - "}\n" + - "Size = 20 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p2() { - Structure struct = getStructure("Z1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + - "pack(2)\n" + - "Structure Z1p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 int:0(0) 1 \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p2x() { - Structure struct = getStructure("Z1p2x"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + - "pack(2)\n" + - "Structure Z1p2x {\n" + - " 0 char 1 a \"\"\n" + - " 2 int:0(0) 1 \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - " 8 short:4(4) 1 d1 \"\"\n" + - " 9 short:4(0) 1 d2 \"\"\n" + - " 9 short:4(4) 1 d3 \"\"\n" + - " 10 short:4(0) 1 d4 \"\"\n" + - " 10 short:4(4) 1 d5 \"\"\n" + - " 11 short:4(0) 1 d6 \"\"\n" + - " 11 short:4(4) 1 d7 \"\"\n" + - " 12 short:0(0) 1 \"\"\n" + - " 12 ushort:6(0) 1 _b \"\"\n" + - " 14 int:8(0) 1 _c \"\"\n" + - " 18 short:4(0) 1 _d \"\"\n" + - " 18 short:4(4) 1 _d1 \"\"\n" + - " 19 short:4(0) 1 _d2 \"\"\n" + - " 19 short:4(4) 1 _d3 \"\"\n" + - " 20 short:4(0) 1 _d4 \"\"\n" + - " 20 short:4(4) 1 _d5 \"\"\n" + - " 21 short:4(0) 1 _d6 \"\"\n" + - " 21 short:4(4) 1 _d7 \"\"\n" + - "}\n" + - "Size = 22 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2p2() { - Structure struct = getStructure("Z2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + - "pack(2)\n" + - "Structure Z2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 int:0(0) 1 \"\"\n" + - " 8 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p2() { - Structure struct = getStructure("Z3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + - "pack(2)\n" + - "Structure Z3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 5 int:4(0) 1 d \"\"\n" + - " 7 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4p2() { - Structure struct = getStructure("Z4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + - "pack(2)\n" + - "Structure Z4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5p2() { - Structure struct = getStructure("Z5p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + - "pack(2)\n" + - "Structure Z5p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:12(0) 2 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x1p2() { - Structure struct = getStructure("x1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + - "pack(2)\n" + - "Structure x1p2 {\n" + - " 0 char 1 a \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x2p2() { - Structure struct = getStructure("x2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + - "pack(2)\n" + - "Structure x2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x3p2() { - Structure struct = getStructure("x3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + - "pack(2)\n" + - "Structure x3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 short:0(0) 1 \"\"\n" + - " 2 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x4p2() { - Structure struct = getStructure("x4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + - "pack(2)\n" + - "Structure x4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 int:27(0) 4 b \"\"\n" + - " 5 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5p4() { - Structure struct = getStructure("Z5p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + - "pack(4)\n" + - "Structure Z5p4 {\n" + - " 0 char 1 a \"\"\n" + - " 2 ushort:12(0) 2 b \"\"\n" + - " 4 int:8(0) 1 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x1p4() { - Structure struct = getStructure("x1p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + - "pack(4)\n" + - "Structure x1p4 {\n" + - " 0 char 1 a \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x2p4() { - Structure struct = getStructure("x2p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + - "pack(4)\n" + - "Structure x2p4 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x3p4() { - Structure struct = getStructure("x3p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + - "pack(4)\n" + - "Structure x3p4 {\n" + - " 0 char 1 a \"\"\n" + - " 4 short:0(0) 1 \"\"\n" + - " 4 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x4p4() { - Structure struct = getStructure("x4p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + - "pack(4)\n" + - "Structure x4p4 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:27(0) 4 b \"\"\n" + - " 7 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsT1() { - Structure struct = getStructure("T1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + - "pack()\n" + - "Structure T1 {\n" + - " 0 charTypedef 1 a \"\"\n" + - " 4 myEnum:3(0) 1 b \"\"\n" + - " 4 enumTypedef:3(3) 1 c \"\"\n" + - " 8 charTypedef:7(0) 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsT2() { - Structure struct = getStructure("T2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + - "pack()\n" + - "Structure T2 {\n" + - " 0 charTypedef 1 a \"\"\n" + - " 4 intTypedef:17(0) 3 b \"\"\n" + - " 6 enumTypedef:3(1) 1 c \"\"\n" + - " 8 charTypedef:3(0) 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1() { - Structure struct = getStructure("S1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + - "pack()\n" + - "Structure S1 {\n" + - " 0 B1 12 b1 \"\"\n" + - " 12 B2 8 b2 \"\"\n" + - " 20 Z1 12 z1 \"\"\n" + - " 32 Z2 12 z2 \"\"\n" + - " 48 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 56 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1p1() { - Structure struct = getStructure("S1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + - "pack(1)\n" + - "Structure S1p1 {\n" + - " 0 B1 12 b1 \"\"\n" + - " 12 B2 8 b2 \"\"\n" + - " 20 Z1 12 z1 \"\"\n" + - " 32 Z2 12 z2 \"\"\n" + - " 44 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 52 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS2p1() { - Structure struct = getStructure("S2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + - "pack(1)\n" + - "Structure S2p1 {\n" + - " 0 B1p1 9 b1p1 \"\"\n" + - " 9 B2p1 7 b2p1 \"\"\n" + - " 16 Z1p1 9 z1p1 \"\"\n" + - " 25 Z2p1 9 z2p1 \"\"\n" + - " 34 Z3p1 7 z3p1 \"\"\n" + - "}\n" + - "Size = 41 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1p2() { - Structure struct = getStructure("S1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + - "pack(2)\n" + - "Structure S1p2 {\n" + - " 0 B1 12 b1 \"\"\n" + - " 12 B2 8 b2 \"\"\n" + - " 20 Z1 12 z1 \"\"\n" + - " 32 Z2 12 z2 \"\"\n" + - " 44 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 52 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS2p2() { - Structure struct = getStructure("S2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + - "pack(2)\n" + - "Structure S2p2 {\n" + - " 0 B1p2 10 b1p2 \"\"\n" + - " 10 B2p2 8 b2p2 \"\"\n" + - " 18 Z1p2 10 z1p2 \"\"\n" + - " 28 Z2p2 10 z2p2 \"\"\n" + - " 38 Z3p2 8 z3p2 \"\"\n" + - "}\n" + - "Size = 46 Actual Alignment = 2", struct); - //@formatter:on - } - +public class MSVCStructureImplBitFieldTest extends MSVCStructureBitFieldTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionBitFieldTest.java new file mode 100644 index 0000000000..622a7e8f4c --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionBitFieldTest.java @@ -0,0 +1,108 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import org.junit.Test; + +public class MSVCUnionBitFieldTest extends AbstractCompositeBitFieldTest { + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (MSVCUnionBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testUnionBitFieldsU1() { + Union struct = getUnion("U1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + + "pack()\n" + + "Union U1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1z() { + Union struct = getUnion("U1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + + "pack()\n" + + "Union U1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 0 \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1() { + Union struct = getUnion("U1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + + "pack(1)\n" + + "Union U1p1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1z() { + Union struct = getUnion("U1p1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + + "pack(1)\n" + + "Union U1p1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 0 \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p2() { + Union struct = getUnion("U1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + + "pack(2)\n" + + "Union U1p2 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java index 43d07140ed..a10bc2174a 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java @@ -15,94 +15,6 @@ */ package ghidra.program.model.data; -import org.junit.Test; - -public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest { - - private static DataTypeManager dataMgr; - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (MSVCUnionImplBitFieldTest.class) { - if (dataMgr == null) { - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg); - dataMgr = new MyDataTypeManager("test", dataOrg); - } - return dataMgr; - } - } - - @Test - public void testUnionBitFieldsU1() { - Union struct = getUnion("U1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + - "pack()\n" + - "Union U1 {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1z() { - Union struct = getUnion("U1z"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + - "pack()\n" + - "Union U1z {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 longlong:0(0) 1 \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p1() { - Union struct = getUnion("U1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + - "pack(1)\n" + - "Union U1p1 {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p1z() { - Union struct = getUnion("U1p1z"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + - "pack(1)\n" + - "Union U1p1z {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 longlong:0(0) 1 \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p2() { - Union struct = getUnion("U1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + - "pack(2)\n" + - "Union U1p2 {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - +public class MSVCUnionImplBitFieldTest extends MSVCUnionBitFieldTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureBigEndianBitFieldTest.java new file mode 100644 index 0000000000..8d216dbf56 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureBigEndianBitFieldTest.java @@ -0,0 +1,831 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class StructureBigEndianBitFieldTest extends AbstractCompositeBitFieldTest { + + // NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2 + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureBitFieldsA1() { + Structure struct = getStructure("A1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + + "pack()\n" + + "Structure A1 {\n" + + " 0 char[5] 5 a \"\"\n" + + " 5 int:3(5) 1 b \"\"\n" + + " 5 int:8(5) 2 c \"\"\n" + + " 6 int:8(5) 2 d \"\"\n" + + " 8 int:6(2) 1 e \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsA2() { + Structure struct = getStructure("A2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + + "pack()\n" + + "Structure A2 {\n" + + " 0 oddStruct 5 a \"\"\n" + + " 5 int:3(5) 1 b \"\"\n" + + " 5 int:8(5) 2 c \"\"\n" + + " 6 int:8(5) 2 d \"\"\n" + + " 8 int:6(2) 1 e \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsA3() { + Structure struct = getStructure("A3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + + "pack()\n" + + "Structure A3 {\n" + + " 0 char[5] 5 a \"\"\n" + + " 5 int:3(5) 1 b \"\"\n" + + " 5 int:8(5) 2 c \"\"\n" + + " 8 int:85(0) 4 d \"\"\n" + + " 12 int:6(2) 1 e \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 4\n", struct); + //@formatter:on + DataTypeComponent c = struct.getComponent(3); + assertTrue(c.isBitFieldComponent()); + BitFieldDataType bfDt = (BitFieldDataType) c.getDataType(); + assertEquals(4, bfDt.getBaseTypeSize()); + assertEquals(32, bfDt.getBitSize()); + assertEquals(85, bfDt.getDeclaredBitSize()); + } + + @Test + public void testStructureBitFieldsB1() { + Structure struct = getStructure("B1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + + "pack()\n" + + "Structure B1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1Flex() { + Structure struct = getStructure("B1flex"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + + "pack()\n" + + "Structure B1flex {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + " 4 long[0] 0 flex \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2() { + Structure struct = getStructure("B2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + + "pack()\n" + + "Structure B2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3() { + Structure struct = getStructure("B3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + + "pack()\n" + + "Structure B3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1() { + Structure struct = getStructure("Z1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + + "pack()\n" + + "Structure Z1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 6 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2() { + Structure struct = getStructure("Z2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + + "pack()\n" + + "Structure Z2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3() { + Structure struct = getStructure("Z3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + + "pack()\n" + + "Structure Z3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4() { + Structure struct = getStructure("Z4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + + "pack()\n" + + "Structure Z4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5() { + Structure struct = getStructure("Z5"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + + "pack()\n" + + "Structure Z5 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 longlong:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ6() { + Structure struct = getStructure("Z6"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + + "pack()\n" + + "Structure Z6 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 longlong:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + " 7 longlong:6(2) 1 e \"\"\n" + + " 8 int:8(0) 1 f \"\"\n" + + " 9 char 1 g \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p1() { + Structure struct = getStructure("B1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + + "pack(1)\n" + + "Structure B1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p1() { + Structure struct = getStructure("B2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + + "pack(1)\n" + + "Structure B2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p1() { + Structure struct = getStructure("B3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + + "pack(1)\n" + + "Structure B3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p1() { + Structure struct = getStructure("Z1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + + "pack(1)\n" + + "Structure Z1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p1() { + Structure struct = getStructure("Z2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + + "pack(1)\n" + + "Structure Z2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 5 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1() { + Structure struct = getStructure("Z3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + + "pack(1)\n" + + "Structure Z3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1T() { + Structure struct = getStructure("Z3p1T"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + + "pack()\n" + + "Structure Z3p1T {\n" + + " 0 char 1 a \"\"\n" + + " 1 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p1() { + Structure struct = getStructure("Z4p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + + "pack(1)\n" + + "Structure Z4p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p2() { + Structure struct = getStructure("B1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + + "pack(2)\n" + + "Structure B1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p2() { + Structure struct = getStructure("B2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + + "pack(2)\n" + + "Structure B2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p2() { + Structure struct = getStructure("B3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + + "pack(2)\n" + + "Structure B3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB4p2() { + Structure struct = getStructure("B4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + + "pack(2)\n" + + "Structure B4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 longlong 8 d \"\"\n" + + " 12 int:4(4) 1 e \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2() { + Structure struct = getStructure("Z1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + + "pack(2)\n" + + "Structure Z1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2x() { + Structure struct = getStructure("Z1p2x"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + + "pack(2)\n" + + "Structure Z1p2x {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 ushort:6(2) 1 b \"\"\n" + + " 4 int:8(2) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + " 6 short:4(2) 1 d1 \"\"\n" + + " 6 short:4(6) 2 d2 \"\"\n" + + " 7 short:4(2) 1 d3 \"\"\n" + + " 7 short:4(6) 2 d4 \"\"\n" + + " 8 short:4(2) 1 d5 \"\"\n" + + " 8 short:4(6) 2 d6 \"\"\n" + + " 9 short:4(2) 1 d7 \"\"\n" + + " 10 short:0(7) 0 \"\"\n" + + " 10 ushort:6(2) 1 _b \"\"\n" + + " 10 int:8(2) 2 _c \"\"\n" + + " 11 short:4(6) 2 _d \"\"\n" + + " 12 short:4(2) 1 _d1 \"\"\n" + + " 12 short:4(6) 2 _d2 \"\"\n" + + " 13 short:4(2) 1 _d3 \"\"\n" + + " 13 short:4(6) 2 _d4 \"\"\n" + + " 14 short:4(2) 1 _d5 \"\"\n" + + " 14 short:4(6) 2 _d6 \"\"\n" + + " 15 short:4(2) 1 _d7 \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p2() { + Structure struct = getStructure("Z2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + + "pack(2)\n" + + "Structure Z2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 4 int:0(7) 0 \"\"\n" + + " 4 short:4(4) 1 d \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p2() { + Structure struct = getStructure("Z3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + + "pack(2)\n" + + "Structure Z3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p2() { + Structure struct = getStructure("Z4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + + "pack(2)\n" + + "Structure Z4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(2) 1 b \"\"\n" + + " 1 int:8(2) 2 c \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p2() { + Structure struct = getStructure("Z5p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + + "pack(2)\n" + + "Structure Z5p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(4) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p2() { + Structure struct = getStructure("x1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + + "pack(2)\n" + + "Structure x1p2 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p2() { + Structure struct = getStructure("x2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + + "pack(2)\n" + + "Structure x2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p2() { + Structure struct = getStructure("x3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + + "pack(2)\n" + + "Structure x3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(7) 0 \"\"\n" + + " 2 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p2() { + Structure struct = getStructure("x4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + + "pack(2)\n" + + "Structure x4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p4() { + Structure struct = getStructure("Z5p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + + "pack(4)\n" + + "Structure Z5p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(4) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p4() { + Structure struct = getStructure("x1p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + + "pack(4)\n" + + "Structure x1p4 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p4() { + Structure struct = getStructure("x2p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + + "pack(4)\n" + + "Structure x2p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p4() { + Structure struct = getStructure("x3p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + + "pack(4)\n" + + "Structure x3p4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(7) 0 \"\"\n" + + " 2 int:27(5) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p4() { + Structure struct = getStructure("x4p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + + "pack(4)\n" + + "Structure x4p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(5) 4 b \"\"\n" + + " 8 longlong:0(7) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT1() { + Structure struct = getStructure("T1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + + "pack()\n" + + "Structure T1 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 myEnum:3(5) 1 b \"\"\n" + + " 1 enumTypedef:3(2) 1 c \"\"\n" + + " 2 charTypedef:7(1) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT2() { + Structure struct = getStructure("T2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + + "pack()\n" + + "Structure T2 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 intTypedef:17(7) 3 b \"\"\n" + + " 3 enumTypedef:3(4) 1 c \"\"\n" + + " 3 charTypedef:3(1) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1() { + Structure struct = getStructure("S1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + + "pack()\n" + + "Structure S1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p1() { + Structure struct = getStructure("S1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + + "pack(1)\n" + + "Structure S1p1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p1() { + Structure struct = getStructure("S2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + + "pack(1)\n" + + "Structure S2p1 {\n" + + " 0 B1p1 4 b1p1 \"\"\n" + + " 4 B2p1 4 b2p1 \"\"\n" + + " 8 Z1p1 7 z1p1 \"\"\n" + + " 15 Z2p1 5 z2p1 \"\"\n" + + " 20 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 28 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p2() { + Structure struct = getStructure("S1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + + "pack(2)\n" + + "Structure S1p2 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p2() { + Structure struct = getStructure("S2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + + "pack(2)\n" + + "Structure S2p2 {\n" + + " 0 B1p2 4 b1p2 \"\"\n" + + " 4 B2p2 4 b2p2 \"\"\n" + + " 8 Z1p2 8 z1p2 \"\"\n" + + " 16 Z2p2 6 z2p2 \"\"\n" + + " 22 Z3p2 8 z3p2 \"\"\n" + + "}\n" + + "Size = 30 Actual Alignment = 2", struct); + //@formatter:on + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java similarity index 78% rename from Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java index a1098f72f1..7333fb16b8 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ -package ghidra.program.database.data; +package ghidra.program.model.data; import static org.junit.Assert.*; +import java.util.List; + import org.apache.commons.compress.utils.Sets; import org.junit.*; import generic.test.AbstractGTest; -import ghidra.program.model.data.*; /** * @@ -45,7 +43,7 @@ public class StructureDataTypeTest extends AbstractGTest { private void transitionToBigEndian() { // transition default little-endian structure to big-endian - DataTypeManager beDtm = new MyBigEndianDataTypeManager(); + DataTypeManager beDtm = createBigEndianDataTypeManager(); struct = struct.clone(beDtm); } @@ -68,6 +66,24 @@ public class StructureDataTypeTest extends AbstractGTest { private Pointer createPointer(DataType dataType, int length) { return new PointerDataType(dataType, length); } + + @Test + public void testEmpty() throws Exception { + Structure s = new StructureDataType("foo", 0); + assertTrue(s.isNotYetDefined()); + assertTrue(s.isZeroLength()); + assertEquals(0, s.getNumComponents()); + assertEquals(0, s.getNumDefinedComponents()); + } + + @Test + public void testSizeOne() throws Exception { + Structure s = new StructureDataType("foo", 1); + assertFalse(s.isNotYetDefined()); + assertFalse(s.isZeroLength()); + assertEquals(1, s.getNumComponents()); + assertEquals(0, s.getNumDefinedComponents()); + } @Test public void testAdd() throws Exception { @@ -84,7 +100,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); + assertEquals("field1_0x1", dtc.getDefaultFieldName()); assertEquals(null, dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -123,7 +139,7 @@ public class StructureDataTypeTest extends AbstractGTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); - assertEquals("field_0x0", dtc.getDefaultFieldName()); + assertEquals("field0_0x0", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertNull(dtc.getComment()); assertEquals(DataType.DEFAULT, dtc.getDataType()); @@ -131,7 +147,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); + assertEquals("field1_0x1", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals(null, dtc.getComment()); @@ -147,7 +163,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(11); assertEquals(11, dtc.getOffset()); assertEquals(11, dtc.getOrdinal()); - assertEquals("field_0xb", dtc.getDefaultFieldName()); + assertEquals("field11_0xb", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); @@ -170,7 +186,7 @@ public class StructureDataTypeTest extends AbstractGTest { DataTypeComponent dtc = struct.getComponent(0); assertEquals(0, dtc.getOffset()); assertEquals(0, dtc.getOrdinal()); - assertEquals("field_0x0", dtc.getDefaultFieldName()); + assertEquals("field0_0x0", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertNull(dtc.getComment()); assertEquals(FloatDataType.class, dtc.getDataType().getClass()); @@ -185,7 +201,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(2); assertEquals(5, dtc.getOffset()); assertEquals(2, dtc.getOrdinal()); - assertEquals("field_0x5", dtc.getDefaultFieldName()); + assertEquals("field2_0x5", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -223,7 +239,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); + assertEquals("field1_0x1", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -245,7 +261,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(4); assertEquals(8, dtc.getOffset()); assertEquals(4, dtc.getOrdinal()); - assertEquals("field_0x8", dtc.getDefaultFieldName()); + assertEquals("field4_0x8", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals(null, dtc.getComment()); assertEquals(FloatDataType.class, dtc.getDataType().getClass()); @@ -269,7 +285,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(1); assertEquals(1, dtc.getOffset()); assertEquals(1, dtc.getOrdinal()); - assertEquals("field_0x1", dtc.getDefaultFieldName()); + assertEquals("field1_0x1", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertEquals("Comment2", dtc.getComment()); assertEquals(WordDataType.class, dtc.getDataType().getClass()); @@ -277,7 +293,7 @@ public class StructureDataTypeTest extends AbstractGTest { dtc = struct.getComponent(2); assertEquals(3, dtc.getOffset()); assertEquals(2, dtc.getOrdinal()); - assertEquals("field_0x3", dtc.getDefaultFieldName()); + assertEquals("field2_0x3", dtc.getDefaultFieldName()); assertNull(dtc.getFieldName()); assertNull(dtc.getComment()); assertEquals(FloatDataType.class, dtc.getDataType().getClass()); @@ -380,7 +396,6 @@ public class StructureDataTypeTest extends AbstractGTest { } - // test inserting at offset 1 @Test public void testInsertAtOffset2() { struct.insertAtOffset(2, new FloatDataType(), 4); @@ -407,12 +422,159 @@ public class StructureDataTypeTest extends AbstractGTest { assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); } + + @Test + public void testInsertWithZeroArrayAtOffset() { + struct.insertAtOffset(2, FloatDataType.dataType, -1); + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + struct.insertAtOffset(2, zeroArray, -1); + assertEquals(13, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(6, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[1].getDataType())); + + assertEquals(2, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertEquals(FloatDataType.class, comps[2].getDataType().getClass()); + + assertEquals(6, comps[3].getOffset()); + assertEquals(4, comps[3].getOrdinal()); + assertEquals(WordDataType.class, comps[3].getDataType().getClass()); + + assertEquals(8, comps[4].getOffset()); + assertEquals(5, comps[4].getOrdinal()); + assertEquals(DWordDataType.class, comps[4].getDataType().getClass()); + + } + + @Test + public void testInsertWithZeroArrayAtOffset2() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + struct.insertAtOffset(2, zeroArray, -1); + struct.insertAtOffset(2, FloatDataType.dataType, -1); + assertEquals(13, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(6, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); + + assertEquals(6, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[2].getDataType())); + + assertEquals(6, comps[3].getOffset()); + assertEquals(4, comps[3].getOrdinal()); + assertEquals(WordDataType.class, comps[3].getDataType().getClass()); + + assertEquals(8, comps[4].getOffset()); + assertEquals(5, comps[4].getOrdinal()); + assertEquals(DWordDataType.class, comps[4].getDataType().getClass()); + + } @Test public void testInsertAtOffsetPastEnd() { struct.insertAtOffset(100, new FloatDataType(), 4); assertEquals(104, struct.getLength()); } + + @Test + public void testSetFlexArray() throws Exception { + + struct.setPackingEnabled(true); + + struct.delete(2); // remove dword to verify flex array alignment below + + DataTypeComponent flexDtc = + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "flex", "FlexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack()\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 byte 1 field4 \"Comment4\"\n" + + " 5 char[0] 0 flex \"FlexComment\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + + struct.replace(flexDtc.getOrdinal(), new ArrayDataType(IntegerDataType.dataType, 0, -1), 0, + "flex", "FlexComment"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack()\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + + " 2 word 2 null \"Comment2\"\n" + + " 4 byte 1 field4 \"Comment4\"\n" + + " 8 int[0] 0 flex \"FlexComment\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testZeroBitFields() throws Exception { + + struct.setPackingEnabled(true); + + struct.delete(2); // remove dword to verify flex array alignment below + + struct.insertBitField(0, 0, 0, IntegerDataType.dataType, 3, "bf2", "bf1Comment"); + struct.insertBitField(0, 0, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment"); + struct.insertBitField(1, 0, 0, IntegerDataType.dataType, 0, "z1", "zero bitfield 1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack()\n" + + "Structure TestStruct {\n" + + " 0 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 4 int:0(0) 0 \"zero bitfield 1\"\n" + + " 4 int:3(0) 1 bf2 \"bf1Comment\"\n" + + " 5 byte 1 field1 \"Comment1\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + + struct.insertBitField(2, 0, 0, IntegerDataType.dataType, 0, "z2", "zero bitfield 2"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack()\n" + + "Structure TestStruct {\n" + + " 0 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 4 int:0(0) 0 \"zero bitfield 1\"\n" + + " 4 int:0(0) 0 \"zero bitfield 2\"\n" + + " 4 int:3(0) 1 bf2 \"bf1Comment\"\n" + + " 5 byte 1 field1 \"Comment1\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } @Test public void testInsertBitFieldLittleEndianAppend() throws Exception { @@ -752,16 +914,6 @@ public class StructureDataTypeTest extends AbstractGTest { assertNull(dtc); } - @Test - public void testReplace() { // bigger, no space below - try { - struct.replace(0, new QWordDataType(), 8); - Assert.fail(); - } - catch (Exception e) { - } - } - @Test public void testReplace1() { // bigger, space below struct.insert(1, new QWordDataType()); @@ -1116,6 +1268,46 @@ public class StructureDataTypeTest extends AbstractGTest { assertEquals(5, comps[1].getOffset()); } + @Test + public void testClearAtOffset() { + + assertEquals(8, struct.getLength()); + + Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1); + struct.insertAtOffset(1, zeroArray, -1); + + assertEquals(8, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[1].getDataType())); + + assertEquals(1, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + assertEquals(3, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); + + struct.clearAtOffset(1); + + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumComponents()); // 2 undefined components replaced word + comps = struct.getDefinedComponents(); + + assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); + assertEquals(3, comps[1].getOffset()); + assertEquals(3, comps[1].getOrdinal()); + } + @Test public void testDeleteAtOffset() { struct.deleteAtOffset(2); @@ -1125,6 +1317,41 @@ public class StructureDataTypeTest extends AbstractGTest { assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); assertEquals(1, comps[1].getOffset()); } + + @Test + public void testDeleteAtOffset2() { + + assertEquals(8, struct.getLength()); + + Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1); + struct.insertAtOffset(1, zeroArray, -1); + + assertEquals(8, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[1].getDataType())); + + assertEquals(1, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + struct.deleteAtOffset(1); + + assertEquals(6, struct.getLength()); + assertEquals(3, struct.getNumComponents()); + comps = struct.getDefinedComponents(); + + assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); + assertEquals(1, comps[1].getOffset()); + } @Test public void testDeleteComponent() { @@ -1250,13 +1477,56 @@ public class StructureDataTypeTest extends AbstractGTest { } @Test - public void testGetComponentAt() { - DataTypeComponent dtc = struct.getComponentAt(4); + public void testgetComponentContaining() { + DataTypeComponent dtc = struct.getComponentContaining(4); assertEquals(DWordDataType.class, dtc.getDataType().getClass()); assertEquals(2, dtc.getOrdinal()); assertEquals(3, dtc.getOffset()); } + @Test + public void testGetComponentsContaining() { + List components = struct.getComponentsContaining(4); + assertEquals("[ 2 3 dword 4 field3 null]", components.toString()); + + 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); + + // force components to align + struct.setPackingEnabled(true); + struct.setPackingEnabled(false); + + assertEquals(16, struct.getLength()); + + /** + * /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 + */ + + // DatatypeComponent.toString: + components = struct.getComponentsContaining(9); + assertEquals("[ 5 9 char[0] 0 zarray1 null, 6 9 undefined 1 null null]", + components.toString()); + + components = struct.getComponentsContaining(10); + assertEquals("[ 7 10 undefined 1 null null]", components.toString()); + + components = struct.getComponentsContaining(16); + assertEquals("[ 10 16 long[0] 0 zarray2 null]", components.toString()); + + } + @Test public void testAddVarLengthDataTypes() { Structure s1 = createStructure("Test1", 0); @@ -1264,7 +1534,7 @@ public class StructureDataTypeTest extends AbstractGTest { s1.add(new StringDataType(), 10); s1.add(new StringDataType(), 15); - DataTypeComponent dtc = s1.getComponentAt(5); + DataTypeComponent dtc = s1.getComponentContaining(5); DataType dt = dtc.getDataType(); assertEquals(-1, dt.getLength()); assertEquals(10, dtc.getLength()); @@ -1273,14 +1543,17 @@ public class StructureDataTypeTest extends AbstractGTest { } @Test - public void testReplaceVarLengthDataTypes() { + public void testReplaceAtVarLengthDataTypes() { + + // TODO: these tests are too simple since they only replace undefined components + Structure s1 = new StructureDataType("Test1", 25); s1.replaceAtOffset(0, new StringDataType(), 5, null, null); s1.replaceAtOffset(5, new StringDataType(), 10, null, null); s1.replaceAtOffset(15, new StringDataType(), 10, null, null); - DataTypeComponent dtc = s1.getComponentAt(5); + DataTypeComponent dtc = s1.getComponentContaining(5); DataType dt = dtc.getDataType(); assertEquals(-1, dt.getLength()); assertEquals(10, dtc.getLength()); @@ -1288,7 +1561,9 @@ public class StructureDataTypeTest extends AbstractGTest { } @Test - public void testReplaceVarLengthDataTypes2() { + public void testReplaceAtVarLengthDataTypes2() { + + // TODO: these tests are too simple since they only replace undefined components Structure s1 = new StructureDataType("Test1", 0x60); s1.replaceAtOffset(0, new StringDataType(), 0xd, null, null); @@ -1301,16 +1576,152 @@ public class StructureDataTypeTest extends AbstractGTest { s1.replaceAtOffset(0x48, new StringDataType(), 0xb, null, null); s1.replaceAtOffset(0x53, new StringDataType(), 0xd, null, null); - DataTypeComponent dtc = s1.getComponentAt(0); + DataTypeComponent dtc = s1.getComponentContaining(0); DataType dt = dtc.getDataType(); assertEquals("string", dt.getDisplayName()); assertEquals(0xd, dtc.getLength()); - dtc = s1.getComponentAt(0x31); + dtc = s1.getComponentContaining(0x31); dt = dtc.getDataType(); assertEquals("string", dt.getDisplayName()); assertEquals(0xd, dtc.getLength()); } + + @Test + public void testReplaceAtPacked() { + + struct.setPackingEnabled(true); // test case where there is no component + + assertEquals(12, struct.getLength()); + assertEquals(4, struct.getNumDefinedComponents()); + + struct.replaceAtOffset(0, DataType.DEFAULT, -1, "a", null); + struct.replaceAtOffset(1, ByteDataType.dataType, -1, "b", null); + struct.replaceAtOffset(2, ByteDataType.dataType, -1, "c", null); + + assertEquals(12, struct.getLength()); + assertEquals(5, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertTrue(Undefined1DataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(2, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[2].getDataType())); + + assertEquals(4, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertTrue(DWordDataType.dataType.isEquivalent(comps[3].getDataType())); + + assertEquals(8, comps[4].getOffset()); + assertEquals(4, comps[4].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); + + } + + @Test + public void testReplaceAt() { + + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumDefinedComponents()); + + struct.replaceAtOffset(0, DataType.DEFAULT, -1, "a", null); + struct.replaceAtOffset(1, ByteDataType.dataType, -1, "b", null); + struct.replaceAtOffset(2, ByteDataType.dataType, -1, "c", null); + struct.replaceAtOffset(4, CharDataType.dataType, -1, "d", null); + + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(1, comps[0].getOffset()); + assertEquals(1, comps[0].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(4, comps[2].getOffset()); + assertEquals(4, comps[2].getOrdinal()); + assertTrue(CharDataType.dataType.isEquivalent(comps[2].getDataType())); + + assertEquals(7, comps[3].getOffset()); + assertEquals(7, comps[3].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[3].getDataType())); + + } + + @Test + public void testReplaceAtWithZeroLength() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + struct.insertAtOffset(3, zeroArray, -1); + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumDefinedComponents()); + + // replace dword with short + struct.replaceAtOffset(3, ShortDataType.dataType, -1, "b", null); + + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(WordDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(3, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[2].getDataType())); + + assertEquals(3, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertTrue(ShortDataType.dataType.isEquivalent(comps[3].getDataType())); + + assertEquals(7, comps[4].getOffset()); + assertEquals(6, comps[4].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); + + Array zeroArray2 = new ArrayDataType(CharDataType.dataType, 0, -1); + + // replace float[0] with char[0] + struct.replaceAtOffset(3, zeroArray2, -1, "a", null); + + comps = struct.getDefinedComponents(); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(WordDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(3, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[2].getDataType())); + + assertEquals(3, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertTrue(zeroArray2.isEquivalent(comps[3].getDataType())); + + assertEquals(7, comps[4].getOffset()); + assertEquals(8, comps[4].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); + } @Test public void testSetName() throws Exception { @@ -1345,7 +1756,7 @@ public class StructureDataTypeTest extends AbstractGTest { s1.add(struct); s1.add(new ByteDataType()); - DataTypeComponent dtc = s1.getComponentAt(7); + DataTypeComponent dtc = s1.getComponentContaining(7); assertEquals(struct, dtc.getDataType()); dtc = s1.getDataTypeAt(7); assertEquals(DWordDataType.class, dtc.getDataType().getClass()); @@ -1388,7 +1799,7 @@ public class StructureDataTypeTest extends AbstractGTest { struct.insertBitFieldAt(9, 2, 7, td, 2, "MyBit3", "bitComment3"); struct.growStructure(1); - struct.setFlexibleArrayComponent(td, "myFlex", "flexComment"); + struct.add(new ArrayDataType(td, 0, -1), "myFlex", "flexComment"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + @@ -1403,12 +1814,12 @@ public class StructureDataTypeTest extends AbstractGTest { " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + // " 11 undefined 1 null \"\"\n" + - " Foo[0] 0 myFlex \"flexComment\"\n" + + " 12 Foo[0] 0 myFlex \"flexComment\"\n" + "}\n" + "Size = 12 Actual Alignment = 1", struct); //@formatter:on - DataTypeManager beDtm = new MyBigEndianDataTypeManager(); + DataTypeManager beDtm = createBigEndianDataTypeManager(); Structure newStruct = new StructureDataType("bigStruct", 0, beDtm); newStruct.replaceWith(struct); @@ -1428,7 +1839,7 @@ public class StructureDataTypeTest extends AbstractGTest { " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + // " 11 undefined 1 null \"\"\n" + - " Foo[0] 0 myFlex \"flexComment\"\n" + + " 12 Foo[0] 0 myFlex \"flexComment\"\n" + "}\n" + "Size = 12 Actual Alignment = 1", newStruct); //@formatter:on @@ -1783,13 +2194,10 @@ public class StructureDataTypeTest extends AbstractGTest { } } - protected class MyBigEndianDataTypeManager extends StandAloneDataTypeManager { - MyBigEndianDataTypeManager() { - super("BEdtm"); - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - dataOrg.setBigEndian(true); - this.dataOrganization = dataOrg; - } + protected DataTypeManager createBigEndianDataTypeManager() { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + dataOrg.setBigEndian(true); + return new StandAloneDataTypeManager("BEdtm", dataOrg); } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java index 038288dc39..6fd26ec9be 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java @@ -15,817 +15,6 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.*; - -import org.junit.Test; - -public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { - - // NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2 - - private static DataTypeManager dataMgr; - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (StructureImplBigEndianBitFieldTest.class) { - if (dataMgr == null) { - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); - dataMgr = new MyDataTypeManager("test", dataOrg); - } - return dataMgr; - } - } - - @Test - public void testStructureBitFieldsA1() { - Structure struct = getStructure("A1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + - "pack()\n" + - "Structure A1 {\n" + - " 0 char[5] 5 a \"\"\n" + - " 5 int:3(5) 1 b \"\"\n" + - " 5 int:8(5) 2 c \"\"\n" + - " 6 int:8(5) 2 d \"\"\n" + - " 8 int:6(2) 1 e \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsA2() { - Structure struct = getStructure("A2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + - "pack()\n" + - "Structure A2 {\n" + - " 0 oddStruct 5 a \"\"\n" + - " 5 int:3(5) 1 b \"\"\n" + - " 5 int:8(5) 2 c \"\"\n" + - " 6 int:8(5) 2 d \"\"\n" + - " 8 int:6(2) 1 e \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsA3() { - Structure struct = getStructure("A3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + - "pack()\n" + - "Structure A3 {\n" + - " 0 char[5] 5 a \"\"\n" + - " 5 int:3(5) 1 b \"\"\n" + - " 5 int:8(5) 2 c \"\"\n" + - " 8 int:85(0) 4 d \"\"\n" + - " 12 int:6(2) 1 e \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 4\n", struct); - //@formatter:on - DataTypeComponent c = struct.getComponent(3); - assertTrue(c.isBitFieldComponent()); - BitFieldDataType bfDt = (BitFieldDataType) c.getDataType(); - assertEquals(4, bfDt.getBaseTypeSize()); - assertEquals(32, bfDt.getBitSize()); - assertEquals(85, bfDt.getDeclaredBitSize()); - } - - @Test - public void testStructureBitFieldsB1() { - Structure struct = getStructure("B1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + - "pack()\n" + - "Structure B1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1Flex() { - Structure struct = getStructure("B1flex"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + - "pack()\n" + - "Structure B1flex {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - " long[0] 0 flex \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2() { - Structure struct = getStructure("B2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + - "pack()\n" + - "Structure B2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3() { - Structure struct = getStructure("B3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + - "pack()\n" + - "Structure B3 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 3 char 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1() { - Structure struct = getStructure("Z1"); - - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + - "pack()\n" + - "Structure Z1 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 ushort:6(2) 1 b \"\"\n" + - " 4 int:8(2) 2 c \"\"\n" + - " 6 short:4(4) 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2() { - Structure struct = getStructure("Z2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + - "pack()\n" + - "Structure Z2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 short:4(4) 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3() { - Structure struct = getStructure("Z3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + - "pack()\n" + - "Structure Z3 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - " 3 longlong:0(7) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4() { - Structure struct = getStructure("Z4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + - "pack()\n" + - "Structure Z4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 8 longlong:0(7) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5() { - Structure struct = getStructure("Z5"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + - "pack()\n" + - "Structure Z5 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 longlong:6(2) 1 b \"\"\n" + - " 4 int:8(2) 2 c \"\"\n" + - " 6 char 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ6() { - Structure struct = getStructure("Z6"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + - "pack()\n" + - "Structure Z6 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 longlong:6(2) 1 b \"\"\n" + - " 4 int:8(2) 2 c \"\"\n" + - " 6 char 1 d \"\"\n" + - " 7 longlong:6(2) 1 e \"\"\n" + - " 8 int:8(0) 1 f \"\"\n" + - " 9 char 1 g \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1p1() { - Structure struct = getStructure("B1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + - "pack(1)\n" + - "Structure B1p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2p1() { - Structure struct = getStructure("B2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + - "pack(1)\n" + - "Structure B2p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3p1() { - Structure struct = getStructure("B3p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + - "pack(1)\n" + - "Structure B3p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 3 char 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p1() { - Structure struct = getStructure("Z1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + - "pack(1)\n" + - "Structure Z1p1 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 ushort:6(2) 1 b \"\"\n" + - " 4 int:8(2) 2 c \"\"\n" + - " 5 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 7 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2p1() { - Structure struct = getStructure("Z2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + - "pack(1)\n" + - "Structure Z2p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 short:4(4) 1 d \"\"\n" + - "}\n" + - "Size = 5 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p1() { - Structure struct = getStructure("Z3p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + - "pack(1)\n" + - "Structure Z3p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - " 3 longlong:0(7) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p1T() { - Structure struct = getStructure("Z3p1T"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + - "pack()\n" + - "Structure Z3p1T {\n" + - " 0 char 1 a \"\"\n" + - " 1 Z3p1 8 z3p1 \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4p1() { - Structure struct = getStructure("Z4p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + - "pack(1)\n" + - "Structure Z4p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 8 longlong:0(7) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1p2() { - Structure struct = getStructure("B1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + - "pack(2)\n" + - "Structure B1p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2p2() { - Structure struct = getStructure("B2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + - "pack(2)\n" + - "Structure B2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3p2() { - Structure struct = getStructure("B3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + - "pack(2)\n" + - "Structure B3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 3 char 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB4p2() { - Structure struct = getStructure("B4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + - "pack(2)\n" + - "Structure B4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 4 longlong 8 d \"\"\n" + - " 12 int:4(4) 1 e \"\"\n" + - "}\n" + - "Size = 14 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p2() { - Structure struct = getStructure("Z1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + - "pack(2)\n" + - "Structure Z1p2 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 ushort:6(2) 1 b \"\"\n" + - " 4 int:8(2) 2 c \"\"\n" + - " 5 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p2x() { - Structure struct = getStructure("Z1p2x"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + - "pack(2)\n" + - "Structure Z1p2x {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 ushort:6(2) 1 b \"\"\n" + - " 4 int:8(2) 2 c \"\"\n" + - " 5 short:4(6) 2 d \"\"\n" + - " 6 short:4(2) 1 d1 \"\"\n" + - " 6 short:4(6) 2 d2 \"\"\n" + - " 7 short:4(2) 1 d3 \"\"\n" + - " 7 short:4(6) 2 d4 \"\"\n" + - " 8 short:4(2) 1 d5 \"\"\n" + - " 8 short:4(6) 2 d6 \"\"\n" + - " 9 short:4(2) 1 d7 \"\"\n" + - " 10 short:0(7) 1 \"\"\n" + - " 10 ushort:6(2) 1 _b \"\"\n" + - " 10 int:8(2) 2 _c \"\"\n" + - " 11 short:4(6) 2 _d \"\"\n" + - " 12 short:4(2) 1 _d1 \"\"\n" + - " 12 short:4(6) 2 _d2 \"\"\n" + - " 13 short:4(2) 1 _d3 \"\"\n" + - " 13 short:4(6) 2 _d4 \"\"\n" + - " 14 short:4(2) 1 _d5 \"\"\n" + - " 14 short:4(6) 2 _d6 \"\"\n" + - " 15 short:4(2) 1 _d7 \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2p2() { - Structure struct = getStructure("Z2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + - "pack(2)\n" + - "Structure Z2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 4 int:0(7) 1 \"\"\n" + - " 4 short:4(4) 1 d \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p2() { - Structure struct = getStructure("Z3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + - "pack(2)\n" + - "Structure Z3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - " 3 longlong:0(7) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4p2() { - Structure struct = getStructure("Z4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + - "pack(2)\n" + - "Structure Z4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(2) 1 b \"\"\n" + - " 1 int:8(2) 2 c \"\"\n" + - " 8 longlong:0(7) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5p2() { - Structure struct = getStructure("Z5p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + - "pack(2)\n" + - "Structure Z5p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:12(4) 2 b \"\"\n" + - " 2 int:8(4) 2 c \"\"\n" + - " 8 longlong:0(7) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x1p2() { - Structure struct = getStructure("x1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + - "pack(2)\n" + - "Structure x1p2 {\n" + - " 0 char 1 a \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x2p2() { - Structure struct = getStructure("x2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + - "pack(2)\n" + - "Structure x2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(5) 4 b \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x3p2() { - Structure struct = getStructure("x3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + - "pack(2)\n" + - "Structure x3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 short:0(7) 1 \"\"\n" + - " 2 int:27(5) 4 b \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x4p2() { - Structure struct = getStructure("x4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + - "pack(2)\n" + - "Structure x4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(5) 4 b \"\"\n" + - " 4 longlong:0(7) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5p4() { - Structure struct = getStructure("Z5p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + - "pack(4)\n" + - "Structure Z5p4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:12(4) 2 b \"\"\n" + - " 2 int:8(4) 2 c \"\"\n" + - " 8 longlong:0(7) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x1p4() { - Structure struct = getStructure("x1p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + - "pack(4)\n" + - "Structure x1p4 {\n" + - " 0 char 1 a \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x2p4() { - Structure struct = getStructure("x2p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + - "pack(4)\n" + - "Structure x2p4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(5) 4 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x3p4() { - Structure struct = getStructure("x3p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + - "pack(4)\n" + - "Structure x3p4 {\n" + - " 0 char 1 a \"\"\n" + - " 2 short:0(7) 1 \"\"\n" + - " 2 int:27(5) 4 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x4p4() { - Structure struct = getStructure("x4p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + - "pack(4)\n" + - "Structure x4p4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(5) 4 b \"\"\n" + - " 4 longlong:0(7) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsT1() { - Structure struct = getStructure("T1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + - "pack()\n" + - "Structure T1 {\n" + - " 0 charTypedef 1 a \"\"\n" + - " 1 myEnum:3(5) 1 b \"\"\n" + - " 1 enumTypedef:3(2) 1 c \"\"\n" + - " 2 charTypedef:7(1) 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsT2() { - Structure struct = getStructure("T2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + - "pack()\n" + - "Structure T2 {\n" + - " 0 charTypedef 1 a \"\"\n" + - " 1 intTypedef:17(7) 3 b \"\"\n" + - " 3 enumTypedef:3(4) 1 c \"\"\n" + - " 3 charTypedef:3(1) 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1() { - Structure struct = getStructure("S1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + - "pack()\n" + - "Structure S1 {\n" + - " 0 B1 4 b1 \"\"\n" + - " 4 B2 4 b2 \"\"\n" + - " 8 Z1 8 z1 \"\"\n" + - " 16 Z2 8 z2 \"\"\n" + - " 24 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 32 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1p1() { - Structure struct = getStructure("S1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + - "pack(1)\n" + - "Structure S1p1 {\n" + - " 0 B1 4 b1 \"\"\n" + - " 4 B2 4 b2 \"\"\n" + - " 8 Z1 8 z1 \"\"\n" + - " 16 Z2 8 z2 \"\"\n" + - " 24 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 32 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS2p1() { - Structure struct = getStructure("S2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + - "pack(1)\n" + - "Structure S2p1 {\n" + - " 0 B1p1 4 b1p1 \"\"\n" + - " 4 B2p1 4 b2p1 \"\"\n" + - " 8 Z1p1 7 z1p1 \"\"\n" + - " 15 Z2p1 5 z2p1 \"\"\n" + - " 20 Z3p1 8 z3p1 \"\"\n" + - "}\n" + - "Size = 28 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1p2() { - Structure struct = getStructure("S1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + - "pack(2)\n" + - "Structure S1p2 {\n" + - " 0 B1 4 b1 \"\"\n" + - " 4 B2 4 b2 \"\"\n" + - " 8 Z1 8 z1 \"\"\n" + - " 16 Z2 8 z2 \"\"\n" + - " 24 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 32 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS2p2() { - Structure struct = getStructure("S2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + - "pack(2)\n" + - "Structure S2p2 {\n" + - " 0 B1p2 4 b1p2 \"\"\n" + - " 4 B2p2 4 b2p2 \"\"\n" + - " 8 Z1p2 8 z1p2 \"\"\n" + - " 16 Z2p2 6 z2p2 \"\"\n" + - " 22 Z3p2 8 z3p2 \"\"\n" + - "}\n" + - "Size = 30 Actual Alignment = 2", struct); - //@formatter:on - } - +public class StructureImplBigEndianBitFieldTest extends StructureBigEndianBitFieldTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java index adb984cfb0..62c4f78f80 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java @@ -15,871 +15,6 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.*; - -import org.junit.Test; - -public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { - - // NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2) - - private static DataTypeManager dataMgr; - - @Override - public void setUp() throws Exception { - super.setUp(); - - // uncomment to generate datatype archive - //writeArchive(); - } - -// private void writeArchive() throws IOException { -// URL resource = ResourceManager.getResource(C_SOURCE_FILE); -// File f = new File(resource.getPath() + ".gdt"); -// if (f.exists()) { -// f.delete(); -// } -// -// FileDataTypeManager fileDtMgr = FileDataTypeManager.createFileArchive(f); -// int txId = fileDtMgr.startTransaction("Save Datatypes"); -// try { -// Iterator composites = getDataTypeManager().getAllComposites(); -// while (composites.hasNext()) { -// fileDtMgr.addDataType(composites.next(), null); -// } -// } -// finally { -// fileDtMgr.endTransaction(txId, true); -// } -// -// fileDtMgr.save(); -// fileDtMgr.close(); -// } - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (StructureImplBigEndianBitFieldTest.class) { - if (dataMgr == null) { - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); - dataMgr = new MyDataTypeManager("test", dataOrg); - } - return dataMgr; - } - } - - @Test - public void testStructureBitFieldsA1() { - Structure struct = getStructure("A1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + - "pack()\n" + - "Structure A1 {\n" + - " 0 char[5] 5 a \"\"\n" + - " 5 int:3(0) 1 b \"\"\n" + - " 5 int:8(3) 2 c \"\"\n" + - " 6 int:8(3) 2 d \"\"\n" + - " 8 int:6(0) 1 e \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsA2() { - Structure struct = getStructure("A2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + - "pack()\n" + - "Structure A2 {\n" + - " 0 oddStruct 5 a \"\"\n" + - " 5 int:3(0) 1 b \"\"\n" + - " 5 int:8(3) 2 c \"\"\n" + - " 6 int:8(3) 2 d \"\"\n" + - " 8 int:6(0) 1 e \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsA3() { - Structure struct = getStructure("A3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + - "pack()\n" + - "Structure A3 {\n" + - " 0 char[5] 5 a \"\"\n" + - " 5 int:3(0) 1 b \"\"\n" + - " 5 int:8(3) 2 c \"\"\n" + - " 8 int:85(0) 4 d \"\"\n" + - " 12 int:6(0) 1 e \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 4\n", struct); - //@formatter:on - DataTypeComponent c = struct.getComponent(3); - assertTrue(c.isBitFieldComponent()); - BitFieldDataType bfDt = (BitFieldDataType) c.getDataType(); - assertEquals(4, bfDt.getBaseTypeSize()); - assertEquals(32, bfDt.getBitSize()); - assertEquals(85, bfDt.getDeclaredBitSize()); - } - - @Test - public void testStructureBitFieldsB1() { - Structure struct = getStructure("B1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + - "pack()\n" + - "Structure B1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1Flex() { - Structure struct = getStructure("B1flex"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + - "pack()\n" + - "Structure B1flex {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - " long[0] 0 flex \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2() { - Structure struct = getStructure("B2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + - "pack()\n" + - "Structure B2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3() { - Structure struct = getStructure("B3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + - "pack()\n" + - "Structure B3 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + // gcc groups with previous non-bitfield - " 1 int:8(6) 2 c \"\"\n" + - " 3 char 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1() { - Structure struct = getStructure("Z1"); - - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + - "pack()\n" + - "Structure Z1 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(6) 2 c \"\"\n" + - " 6 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2() { - Structure struct = getStructure("Z2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + - "pack()\n" + - "Structure Z2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3() { - Structure struct = getStructure("Z3"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + - "pack()\n" + - "Structure Z3 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - " 3 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4() { - Structure struct = getStructure("Z4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + - "pack()\n" + - "Structure Z4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5() { - Structure struct = getStructure("Z5"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + - "pack()\n" + - "Structure Z5 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 longlong:6(0) 1 b \"\"\n" + - " 4 int:8(6) 2 c \"\"\n" + - " 6 char 1 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ6() { - Structure struct = getStructure("Z6"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + - "pack()\n" + - "Structure Z6 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 longlong:6(0) 1 b \"\"\n" + - " 4 int:8(6) 2 c \"\"\n" + - " 6 char 1 d \"\"\n" + - " 7 longlong:6(0) 1 e \"\"\n" + - " 8 int:8(0) 1 f \"\"\n" + - " 9 char 1 g \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1p1() { - Structure struct = getStructure("B1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + - "pack(1)\n" + - "Structure B1p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2p1() { - Structure struct = getStructure("B2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + - "pack(1)\n" + - "Structure B2p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3p1() { - Structure struct = getStructure("B3p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + - "pack(1)\n" + - "Structure B3p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 3 char 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p1() { - Structure struct = getStructure("Z1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + - "pack(1)\n" + - "Structure Z1p1 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(6) 2 c \"\"\n" + - " 5 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 7 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2p1() { - Structure struct = getStructure("Z2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + - "pack(1)\n" + - "Structure Z2p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 5 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p1() { - Structure struct = getStructure("Z3p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + - "pack(1)\n" + - "Structure Z3p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - " 3 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p1T() { - Structure struct = getStructure("Z3p1T"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + - "pack()\n" + - "Structure Z3p1T {\n" + - " 0 char 1 a \"\"\n" + - " 1 Z3p1 8 z3p1 \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4p1() { - Structure struct = getStructure("Z4p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + - "pack(1)\n" + - "Structure Z4p1 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 9 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB1p2() { - Structure struct = getStructure("B1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + - "pack(2)\n" + - "Structure B1p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB2p2() { - Structure struct = getStructure("B2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + - "pack(2)\n" + - "Structure B2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB3p2() { - Structure struct = getStructure("B3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + - "pack(2)\n" + - "Structure B3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 3 char 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsB4p2() { - Structure struct = getStructure("B4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + - "pack(2)\n" + - "Structure B4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 4 longlong 8 d \"\"\n" + - " 12 int:4(0) 1 e \"\"\n" + - "}\n" + - "Size = 14 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p2() { - Structure struct = getStructure("Z1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + - "pack(2)\n" + - "Structure Z1p2 {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(6) 2 c \"\"\n" + - " 5 short:4(6) 2 d \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ1p2x() { - Structure struct = getStructure("Z1p2x"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + - "pack(2)\n" + - "Structure Z1p2x {\n" + - " 0 char 1 a \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 ushort:6(0) 1 b \"\"\n" + - " 4 int:8(6) 2 c \"\"\n" + - " 5 short:4(6) 2 d \"\"\n" + - " 6 short:4(2) 1 d1 \"\"\n" + - " 6 short:4(6) 2 d2 \"\"\n" + - " 7 short:4(2) 1 d3 \"\"\n" + - " 7 short:4(6) 2 d4 \"\"\n" + - " 8 short:4(2) 1 d5 \"\"\n" + - " 8 short:4(6) 2 d6 \"\"\n" + - " 9 short:4(2) 1 d7 \"\"\n" + - " 10 short:0(0) 1 \"\"\n" + - " 10 ushort:6(0) 1 _b \"\"\n" + - " 10 int:8(6) 2 _c \"\"\n" + - " 11 short:4(6) 2 _d \"\"\n" + - " 12 short:4(2) 1 _d1 \"\"\n" + - " 12 short:4(6) 2 _d2 \"\"\n" + - " 13 short:4(2) 1 _d3 \"\"\n" + - " 13 short:4(6) 2 _d4 \"\"\n" + - " 14 short:4(2) 1 _d5 \"\"\n" + - " 14 short:4(6) 2 _d6 \"\"\n" + - " 15 short:4(2) 1 _d7 \"\"\n" + - "}\n" + - "Size = 16 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ2p2() { - Structure struct = getStructure("Z2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + - "pack(2)\n" + - "Structure Z2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 4 int:0(0) 1 \"\"\n" + - " 4 short:4(0) 1 d \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ3p2() { - Structure struct = getStructure("Z3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + - "pack(2)\n" + - "Structure Z3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 2 int:4(6) 2 d \"\"\n" + - " 3 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ4p2() { - Structure struct = getStructure("Z4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + - "pack(2)\n" + - "Structure Z4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:6(0) 1 b \"\"\n" + - " 1 int:8(6) 2 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5p2() { - Structure struct = getStructure("Z5p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + - "pack(2)\n" + - "Structure Z5p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:12(0) 2 b \"\"\n" + - " 2 int:8(4) 2 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 10 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x1p2() { - Structure struct = getStructure("x1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + - "pack(2)\n" + - "Structure x1p2 {\n" + - " 0 char 1 a \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x2p2() { - Structure struct = getStructure("x2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + - "pack(2)\n" + - "Structure x2p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x3p2() { - Structure struct = getStructure("x3p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + - "pack(2)\n" + - "Structure x3p2 {\n" + - " 0 char 1 a \"\"\n" + - " 2 short:0(0) 1 \"\"\n" + - " 2 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 6 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x4p2() { - Structure struct = getStructure("x4p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + - "pack(2)\n" + - "Structure x4p2 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(0) 4 b \"\"\n" + - " 4 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsZ5p4() { - Structure struct = getStructure("Z5p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + - "pack(4)\n" + - "Structure Z5p4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 ushort:12(0) 2 b \"\"\n" + - " 2 int:8(4) 2 c \"\"\n" + - " 8 longlong:0(0) 1 \"\"\n" + - " 8 char 1 d \"\"\n" + - "}\n" + - "Size = 12 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x1p4() { - Structure struct = getStructure("x1p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + - "pack(4)\n" + - "Structure x1p4 {\n" + - " 0 char 1 a \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x2p4() { - Structure struct = getStructure("x2p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + - "pack(4)\n" + - "Structure x2p4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x3p4() { - Structure struct = getStructure("x3p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + - "pack(4)\n" + - "Structure x3p4 {\n" + - " 0 char 1 a \"\"\n" + - " 2 short:0(0) 1 \"\"\n" + - " 2 int:27(0) 4 b \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFields_x4p4() { - Structure struct = getStructure("x4p4"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + - "pack(4)\n" + - "Structure x4p4 {\n" + - " 0 char 1 a \"\"\n" + - " 1 int:27(0) 4 b \"\"\n" + - " 4 longlong:0(0) 1 \"\"\n" + - "}\n" + - "Size = 8 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsT1() { - Structure struct = getStructure("T1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + - "pack()\n" + - "Structure T1 {\n" + - " 0 charTypedef 1 a \"\"\n" + - " 1 myEnum:3(0) 1 b \"\"\n" + - " 1 enumTypedef:3(3) 1 c \"\"\n" + - " 2 charTypedef:7(0) 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsT2() { - Structure struct = getStructure("T2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + - "pack()\n" + - "Structure T2 {\n" + - " 0 charTypedef 1 a \"\"\n" + - " 1 intTypedef:17(0) 3 b \"\"\n" + - " 3 enumTypedef:3(1) 1 c \"\"\n" + - " 3 charTypedef:3(4) 1 d \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1() { - Structure struct = getStructure("S1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + - "pack()\n" + - "Structure S1 {\n" + - " 0 B1 4 b1 \"\"\n" + - " 4 B2 4 b2 \"\"\n" + - " 8 Z1 8 z1 \"\"\n" + - " 16 Z2 8 z2 \"\"\n" + - " 24 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 32 Actual Alignment = 8", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1p1() { - Structure struct = getStructure("S1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + - "pack(1)\n" + - "Structure S1p1 {\n" + - " 0 B1 4 b1 \"\"\n" + - " 4 B2 4 b2 \"\"\n" + - " 8 Z1 8 z1 \"\"\n" + - " 16 Z2 8 z2 \"\"\n" + - " 24 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 32 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS2p1() { - Structure struct = getStructure("S2p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + - "pack(1)\n" + - "Structure S2p1 {\n" + - " 0 B1p1 4 b1p1 \"\"\n" + - " 4 B2p1 4 b2p1 \"\"\n" + - " 8 Z1p1 7 z1p1 \"\"\n" + - " 15 Z2p1 5 z2p1 \"\"\n" + - " 20 Z3p1 8 z3p1 \"\"\n" + - "}\n" + - "Size = 28 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS1p2() { - Structure struct = getStructure("S1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + - "pack(2)\n" + - "Structure S1p2 {\n" + - " 0 B1 4 b1 \"\"\n" + - " 4 B2 4 b2 \"\"\n" + - " 8 Z1 8 z1 \"\"\n" + - " 16 Z2 8 z2 \"\"\n" + - " 24 Z3 8 z3 \"\"\n" + - "}\n" + - "Size = 32 Actual Alignment = 2", struct); - //@formatter:on - } - - @Test - public void testStructureBitFieldsS2p2() { - Structure struct = getStructure("S2p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + - "pack(2)\n" + - "Structure S2p2 {\n" + - " 0 B1p2 4 b1p2 \"\"\n" + - " 4 B2p2 4 b2p2 \"\"\n" + - " 8 Z1p2 8 z1p2 \"\"\n" + - " 16 Z2p2 6 z2p2 \"\"\n" + - " 22 Z3p2 8 z3p2 \"\"\n" + - "}\n" + - "Size = 30 Actual Alignment = 2", struct); - //@formatter:on - } - -// @Test -// public void testStructureBitFieldsFOO() { -// Structure struct = getStructure("Z3p1T"); -// -// System.out.println(struct.toString()); -// -// DataTypeManager dtm = struct.getDataTypeManager(); -// if (dtm instanceof StandAloneDataTypeManager) { -// ((StandAloneDataTypeManager) dtm).startTransaction("TEST"); -// } -// else if (dtm instanceof ProgramDataTypeManager) { -// ((ProgramDataTypeManager) dtm).getProgram().startTransaction("TEST"); -// } -// -// ArrayList components = -// (ArrayList) getInstanceField("components", struct); -// -// AlignedStructurePacker.packComponents(struct, components); -// -// //@formatter:off -// CompositeTestUtils.assertExpectedComposite(this, "", struct); -// //@formatter:on -// } +public class StructureImplLittleEndianBitFieldTest extends StructureLittleEndianBitFieldTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureLittleEndianBitFieldTest.java new file mode 100644 index 0000000000..52efe6ac47 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureLittleEndianBitFieldTest.java @@ -0,0 +1,885 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class StructureLittleEndianBitFieldTest extends AbstractCompositeBitFieldTest { + + // NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2) + + private static DataTypeManager dataMgr; + + @Override + public void setUp() throws Exception { + super.setUp(); + + // uncomment to generate datatype archive + //writeArchive(); + } + +// private void writeArchive() throws IOException { +// URL resource = ResourceManager.getResource(C_SOURCE_FILE); +// File f = new File(resource.getPath() + ".gdt"); +// if (f.exists()) { +// f.delete(); +// } +// +// FileDataTypeManager fileDtMgr = FileDataTypeManager.createFileArchive(f); +// int txId = fileDtMgr.startTransaction("Save Datatypes"); +// try { +// Iterator composites = getDataTypeManager().getAllComposites(); +// while (composites.hasNext()) { +// fileDtMgr.addDataType(composites.next(), null); +// } +// } +// finally { +// fileDtMgr.endTransaction(txId, true); +// } +// +// fileDtMgr.save(); +// fileDtMgr.close(); +// } + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testStructureBitFieldsA1() { + Structure struct = getStructure("A1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + + "pack()\n" + + "Structure A1 {\n" + + " 0 char[5] 5 a \"\"\n" + + " 5 int:3(0) 1 b \"\"\n" + + " 5 int:8(3) 2 c \"\"\n" + + " 6 int:8(3) 2 d \"\"\n" + + " 8 int:6(0) 1 e \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsA2() { + Structure struct = getStructure("A2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + + "pack()\n" + + "Structure A2 {\n" + + " 0 oddStruct 5 a \"\"\n" + + " 5 int:3(0) 1 b \"\"\n" + + " 5 int:8(3) 2 c \"\"\n" + + " 6 int:8(3) 2 d \"\"\n" + + " 8 int:6(0) 1 e \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsA3() { + Structure struct = getStructure("A3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + + "pack()\n" + + "Structure A3 {\n" + + " 0 char[5] 5 a \"\"\n" + + " 5 int:3(0) 1 b \"\"\n" + + " 5 int:8(3) 2 c \"\"\n" + + " 8 int:85(0) 4 d \"\"\n" + + " 12 int:6(0) 1 e \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 4\n", struct); + //@formatter:on + DataTypeComponent c = struct.getComponent(3); + assertTrue(c.isBitFieldComponent()); + BitFieldDataType bfDt = (BitFieldDataType) c.getDataType(); + assertEquals(4, bfDt.getBaseTypeSize()); + assertEquals(32, bfDt.getBitSize()); + assertEquals(85, bfDt.getDeclaredBitSize()); + } + + @Test + public void testStructureBitFieldsB1() { + Structure struct = getStructure("B1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + + "pack()\n" + + "Structure B1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1Flex() { + Structure struct = getStructure("B1flex"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + + "pack()\n" + + "Structure B1flex {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + " 8 long[0] 0 flex \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2() { + Structure struct = getStructure("B2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + + "pack()\n" + + "Structure B2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3() { + Structure struct = getStructure("B3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + + "pack()\n" + + "Structure B3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + // gcc groups with previous non-bitfield + " 1 int:8(6) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1() { + Structure struct = getStructure("Z1"); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + + "pack()\n" + + "Structure Z1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 6 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2() { + Structure struct = getStructure("Z2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + + "pack()\n" + + "Structure Z2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3() { + Structure struct = getStructure("Z3"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + + "pack()\n" + + "Structure Z3 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4() { + Structure struct = getStructure("Z4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + + "pack()\n" + + "Structure Z4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5() { + Structure struct = getStructure("Z5"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + + "pack()\n" + + "Structure Z5 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 longlong:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ6() { + Structure struct = getStructure("Z6"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + + "pack()\n" + + "Structure Z6 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 longlong:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 6 char 1 d \"\"\n" + + " 7 longlong:6(0) 1 e \"\"\n" + + " 8 int:8(0) 1 f \"\"\n" + + " 9 char 1 g \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p1() { + Structure struct = getStructure("B1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + + "pack(1)\n" + + "Structure B1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p1() { + Structure struct = getStructure("B2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + + "pack(1)\n" + + "Structure B2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p1() { + Structure struct = getStructure("B3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + + "pack(1)\n" + + "Structure B3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p1() { + Structure struct = getStructure("Z1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + + "pack(1)\n" + + "Structure Z1p1 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 7 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p1() { + Structure struct = getStructure("Z2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + + "pack(1)\n" + + "Structure Z2p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 5 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1() { + Structure struct = getStructure("Z3p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + + "pack(1)\n" + + "Structure Z3p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p1T() { + Structure struct = getStructure("Z3p1T"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + + "pack()\n" + + "Structure Z3p1T {\n" + + " 0 char 1 a \"\"\n" + + " 1 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p1() { + Structure struct = getStructure("Z4p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + + "pack(1)\n" + + "Structure Z4p1 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 9 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB1p2() { + Structure struct = getStructure("B1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + + "pack(2)\n" + + "Structure B1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB2p2() { + Structure struct = getStructure("B2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + + "pack(2)\n" + + "Structure B2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB3p2() { + Structure struct = getStructure("B3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + + "pack(2)\n" + + "Structure B3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 3 char 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsB4p2() { + Structure struct = getStructure("B4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + + "pack(2)\n" + + "Structure B4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 longlong 8 d \"\"\n" + + " 12 int:4(0) 1 e \"\"\n" + + "}\n" + + "Size = 14 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2() { + Structure struct = getStructure("Z1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + + "pack(2)\n" + + "Structure Z1p2 {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ1p2x() { + Structure struct = getStructure("Z1p2x"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + + "pack(2)\n" + + "Structure Z1p2x {\n" + + " 0 char 1 a \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 ushort:6(0) 1 b \"\"\n" + + " 4 int:8(6) 2 c \"\"\n" + + " 5 short:4(6) 2 d \"\"\n" + + " 6 short:4(2) 1 d1 \"\"\n" + + " 6 short:4(6) 2 d2 \"\"\n" + + " 7 short:4(2) 1 d3 \"\"\n" + + " 7 short:4(6) 2 d4 \"\"\n" + + " 8 short:4(2) 1 d5 \"\"\n" + + " 8 short:4(6) 2 d6 \"\"\n" + + " 9 short:4(2) 1 d7 \"\"\n" + + " 10 short:0(0) 0 \"\"\n" + + " 10 ushort:6(0) 1 _b \"\"\n" + + " 10 int:8(6) 2 _c \"\"\n" + + " 11 short:4(6) 2 _d \"\"\n" + + " 12 short:4(2) 1 _d1 \"\"\n" + + " 12 short:4(6) 2 _d2 \"\"\n" + + " 13 short:4(2) 1 _d3 \"\"\n" + + " 13 short:4(6) 2 _d4 \"\"\n" + + " 14 short:4(2) 1 _d5 \"\"\n" + + " 14 short:4(6) 2 _d6 \"\"\n" + + " 15 short:4(2) 1 _d7 \"\"\n" + + "}\n" + + "Size = 16 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ2p2() { + Structure struct = getStructure("Z2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + + "pack(2)\n" + + "Structure Z2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 4 int:0(0) 0 \"\"\n" + + " 4 short:4(0) 1 d \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ3p2() { + Structure struct = getStructure("Z3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + + "pack(2)\n" + + "Structure Z3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 2 int:4(6) 2 d \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ4p2() { + Structure struct = getStructure("Z4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + + "pack(2)\n" + + "Structure Z4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:6(0) 1 b \"\"\n" + + " 1 int:8(6) 2 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p2() { + Structure struct = getStructure("Z5p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + + "pack(2)\n" + + "Structure Z5p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(0) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 10 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p2() { + Structure struct = getStructure("x1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + + "pack(2)\n" + + "Structure x1p2 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p2() { + Structure struct = getStructure("x2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + + "pack(2)\n" + + "Structure x2p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p2() { + Structure struct = getStructure("x3p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + + "pack(2)\n" + + "Structure x3p2 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(0) 0 \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 6 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p2() { + Structure struct = getStructure("x4p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + + "pack(2)\n" + + "Structure x4p2 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsZ5p4() { + Structure struct = getStructure("Z5p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + + "pack(4)\n" + + "Structure Z5p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 ushort:12(0) 2 b \"\"\n" + + " 2 int:8(4) 2 c \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + " 8 char 1 d \"\"\n" + + "}\n" + + "Size = 12 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x1p4() { + Structure struct = getStructure("x1p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + + "pack(4)\n" + + "Structure x1p4 {\n" + + " 0 char 1 a \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x2p4() { + Structure struct = getStructure("x2p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + + "pack(4)\n" + + "Structure x2p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x3p4() { + Structure struct = getStructure("x3p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + + "pack(4)\n" + + "Structure x3p4 {\n" + + " 0 char 1 a \"\"\n" + + " 2 short:0(0) 0 \"\"\n" + + " 2 int:27(0) 4 b \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFields_x4p4() { + Structure struct = getStructure("x4p4"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + + "pack(4)\n" + + "Structure x4p4 {\n" + + " 0 char 1 a \"\"\n" + + " 1 int:27(0) 4 b \"\"\n" + + " 8 longlong:0(0) 0 \"\"\n" + + "}\n" + + "Size = 8 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT1() { + Structure struct = getStructure("T1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + + "pack()\n" + + "Structure T1 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 myEnum:3(0) 1 b \"\"\n" + + " 1 enumTypedef:3(3) 1 c \"\"\n" + + " 2 charTypedef:7(0) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsT2() { + Structure struct = getStructure("T2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + + "pack()\n" + + "Structure T2 {\n" + + " 0 charTypedef 1 a \"\"\n" + + " 1 intTypedef:17(0) 3 b \"\"\n" + + " 3 enumTypedef:3(1) 1 c \"\"\n" + + " 3 charTypedef:3(4) 1 d \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1() { + Structure struct = getStructure("S1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + + "pack()\n" + + "Structure S1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 8", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p1() { + Structure struct = getStructure("S1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + + "pack(1)\n" + + "Structure S1p1 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p1() { + Structure struct = getStructure("S2p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + + "pack(1)\n" + + "Structure S2p1 {\n" + + " 0 B1p1 4 b1p1 \"\"\n" + + " 4 B2p1 4 b2p1 \"\"\n" + + " 8 Z1p1 7 z1p1 \"\"\n" + + " 15 Z2p1 5 z2p1 \"\"\n" + + " 20 Z3p1 8 z3p1 \"\"\n" + + "}\n" + + "Size = 28 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS1p2() { + Structure struct = getStructure("S1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + + "pack(2)\n" + + "Structure S1p2 {\n" + + " 0 B1 4 b1 \"\"\n" + + " 4 B2 4 b2 \"\"\n" + + " 8 Z1 8 z1 \"\"\n" + + " 16 Z2 8 z2 \"\"\n" + + " 24 Z3 8 z3 \"\"\n" + + "}\n" + + "Size = 32 Actual Alignment = 2", struct); + //@formatter:on + } + + @Test + public void testStructureBitFieldsS2p2() { + Structure struct = getStructure("S2p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + + "pack(2)\n" + + "Structure S2p2 {\n" + + " 0 B1p2 4 b1p2 \"\"\n" + + " 4 B2p2 4 b2p2 \"\"\n" + + " 8 Z1p2 8 z1p2 \"\"\n" + + " 16 Z2p2 6 z2p2 \"\"\n" + + " 22 Z3p2 8 z3p2 \"\"\n" + + "}\n" + + "Size = 30 Actual Alignment = 2", struct); + //@formatter:on + } + +// @Test +// public void testStructureBitFieldsFOO() { +// Structure struct = getStructure("Z3p1T"); +// +// System.out.println(struct.toString()); +// +// DataTypeManager dtm = struct.getDataTypeManager(); +// if (dtm instanceof StandAloneDataTypeManager) { +// ((StandAloneDataTypeManager) dtm).startTransaction("TEST"); +// } +// else if (dtm instanceof ProgramDataTypeManager) { +// ((ProgramDataTypeManager) dtm).getProgram().startTransaction("TEST"); +// } +// +// ArrayList components = +// (ArrayList) getInstanceField("components", struct); +// +// AlignedStructurePacker.packComponents(struct, components); +// +// //@formatter:off +// CompositeTestUtils.assertExpectedComposite(this, "", struct); +// //@formatter:on +// } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionBigEndianBitFieldTest.java new file mode 100644 index 0000000000..387e185f48 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionBigEndianBitFieldTest.java @@ -0,0 +1,109 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import org.junit.Test; + +public class UnionBigEndianBitFieldTest extends AbstractCompositeBitFieldTest { + + // NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2 + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testUnionBitFieldsU1() { + Union struct = getUnion("U1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + + "pack()\n" + + "Union U1 {\n" + + " 0 int:4(4) 1 a \"\"\n" + + " 0 int:2(6) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1z() { + Union struct = getUnion("U1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + + "pack()\n" + + "Union U1z {\n" + + " 0 int:4(4) 1 a \"\"\n" + + " 0 longlong:0(7) 0 \"\"\n" + // has no impact + " 0 int:2(6) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1() { + Union struct = getUnion("U1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + + "pack(1)\n" + + "Union U1p1 {\n" + + " 0 int:4(4) 1 a \"\"\n" + + " 0 int:2(6) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1z() { + Union struct = getUnion("U1p1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + + "pack(1)\n" + + "Union U1p1z {\n" + + " 0 int:4(4) 1 a \"\"\n" + + " 0 longlong:0(7) 0 \"\"\n" + // has no impact + " 0 int:2(6) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p2() { + Union struct = getUnion("U1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + + "pack(2)\n" + + "Union U1p2 {\n" + + " 0 int:4(4) 1 a \"\"\n" + + " 0 int:2(6) 1 b \"\"\n" + + "}\n" + + "Size = 2 Actual Alignment = 2", struct); + //@formatter:on + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java similarity index 97% rename from Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java rename to Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java index 1ab193bacc..0937e2fc48 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java @@ -13,10 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ -package ghidra.program.database.data; +package ghidra.program.model.data; import static org.junit.Assert.*; @@ -25,7 +22,6 @@ import org.junit.*; import com.google.common.collect.Sets; import generic.test.AbstractGTest; -import ghidra.program.model.data.*; /** * @@ -46,8 +42,8 @@ public class UnionDataTypeTest extends AbstractGTest { private void transitionToBigEndian() { // transition default little-endian structure to big-endian - DataTypeManager beDtm = new MyBigEndianDataTypeManager(); - union = (Union) union.clone(beDtm); + DataTypeManager beDtm = createBigEndianDataTypeManager(); + union = union.clone(beDtm); } private Union createUnion(String name) { @@ -594,12 +590,9 @@ public class UnionDataTypeTest extends AbstractGTest { } } - protected class MyBigEndianDataTypeManager extends StandAloneDataTypeManager { - MyBigEndianDataTypeManager() { - super("BEdtm"); - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - dataOrg.setBigEndian(true); - this.dataOrganization = dataOrg; - } + protected DataTypeManager createBigEndianDataTypeManager() { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + dataOrg.setBigEndian(true); + return new StandAloneDataTypeManager("BEdtm", dataOrg); } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java index 89316bea5a..ce7599dd20 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java @@ -15,95 +15,6 @@ */ package ghidra.program.model.data; -import org.junit.Test; - -public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { - - // NOTE: verified bitfields sample built with mips-elf-gcc (GCC) 4.9.2 - - private static DataTypeManager dataMgr; - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (StructureImplBigEndianBitFieldTest.class) { - if (dataMgr == null) { - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - DataOrganizationTestUtils.initDataOrganization32BitMips(dataOrg); - dataMgr = new MyDataTypeManager("test", dataOrg); - } - return dataMgr; - } - } - - @Test - public void testUnionBitFieldsU1() { - Union struct = getUnion("U1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + - "pack()\n" + - "Union U1 {\n" + - " 0 int:4(4) 1 a \"\"\n" + - " 0 int:2(6) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1z() { - Union struct = getUnion("U1z"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + - "pack()\n" + - "Union U1z {\n" + - " 0 int:4(4) 1 a \"\"\n" + - " 0 longlong:0(7) 1 \"\"\n" + // has no impact - " 0 int:2(6) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p1() { - Union struct = getUnion("U1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + - "pack(1)\n" + - "Union U1p1 {\n" + - " 0 int:4(4) 1 a \"\"\n" + - " 0 int:2(6) 1 b \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p1z() { - Union struct = getUnion("U1p1z"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + - "pack(1)\n" + - "Union U1p1z {\n" + - " 0 int:4(4) 1 a \"\"\n" + - " 0 longlong:0(7) 1 \"\"\n" + // has no impact - " 0 int:2(6) 1 b \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p2() { - Union struct = getUnion("U1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + - "pack(2)\n" + - "Union U1p2 {\n" + - " 0 int:4(4) 1 a \"\"\n" + - " 0 int:2(6) 1 b \"\"\n" + - "}\n" + - "Size = 2 Actual Alignment = 2", struct); - //@formatter:on - } +public class UnionImplBigEndianBitFieldTest extends UnionBigEndianBitFieldTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java index 964902b10d..5e348feb32 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java @@ -15,95 +15,6 @@ */ package ghidra.program.model.data; -import org.junit.Test; - -public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitFieldTest { - - // NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2) - - private static DataTypeManager dataMgr; - - @Override - protected DataTypeManager getDataTypeManager() { - synchronized (StructureImplBigEndianBitFieldTest.class) { - if (dataMgr == null) { - DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); - DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); - dataMgr = new MyDataTypeManager("test", dataOrg); - } - return dataMgr; - } - } - - @Test - public void testUnionBitFieldsU1() { - Union struct = getUnion("U1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + - "pack()\n" + - "Union U1 {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1z() { - Union struct = getUnion("U1z"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + - "pack()\n" + - "Union U1z {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 longlong:0(0) 1 \"\"\n" + // has no impact - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 4 Actual Alignment = 4", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p1() { - Union struct = getUnion("U1p1"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + - "pack(1)\n" + - "Union U1p1 {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p1z() { - Union struct = getUnion("U1p1z"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + - "pack(1)\n" + - "Union U1p1z {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 longlong:0(0) 1 \"\"\n" + // has no impact - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 1 Actual Alignment = 1", struct); - //@formatter:on - } - - @Test - public void testUnionBitFieldsU1p2() { - Union struct = getUnion("U1p2"); - //@formatter:off - CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + - "pack(2)\n" + - "Union U1p2 {\n" + - " 0 int:4(0) 1 a \"\"\n" + - " 0 int:2(0) 1 b \"\"\n" + - "}\n" + - "Size = 2 Actual Alignment = 2", struct); - //@formatter:on - } +public class UnionImplLittleEndianBitFieldTest extends UnionLittleEndianBitFieldTest { + // "Impl" in class names forces StructureDB to be copied to Impl types for verification } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionLittleEndianBitFieldTest.java new file mode 100644 index 0000000000..4f83bed15d --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionLittleEndianBitFieldTest.java @@ -0,0 +1,109 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.data; + +import org.junit.Test; + +public class UnionLittleEndianBitFieldTest extends AbstractCompositeBitFieldTest { + + // NOTE: verified bitfields sample built with Apple LLVM version 9.0.0 (clang-900.0.39.2) + + private static DataTypeManager dataMgr; + + @Override + protected DataTypeManager getDataTypeManager() { + synchronized (StructureBigEndianBitFieldTest.class) { + if (dataMgr == null) { + DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null); + DataOrganizationTestUtils.initDataOrganizationGcc64BitX86(dataOrg); + dataMgr = createDataTypeManager("test", dataOrg); + } + return dataMgr; + } + } + + @Test + public void testUnionBitFieldsU1() { + Union struct = getUnion("U1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + + "pack()\n" + + "Union U1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1z() { + Union struct = getUnion("U1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + + "pack()\n" + + "Union U1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 0 \"\"\n" + // has no impact + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 4 Actual Alignment = 4", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1() { + Union struct = getUnion("U1p1"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + + "pack(1)\n" + + "Union U1p1 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p1z() { + Union struct = getUnion("U1p1z"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + + "pack(1)\n" + + "Union U1p1z {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 longlong:0(0) 0 \"\"\n" + // has no impact + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 1 Actual Alignment = 1", struct); + //@formatter:on + } + + @Test + public void testUnionBitFieldsU1p2() { + Union struct = getUnion("U1p2"); + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + + "pack(2)\n" + + "Union U1p2 {\n" + + " 0 int:4(0) 1 a \"\"\n" + + " 0 int:2(0) 1 b \"\"\n" + + "}\n" + + "Size = 2 Actual Alignment = 2", struct); + //@formatter:on + } +} diff --git a/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/zeroarray.h b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/zeroarray.h new file mode 100644 index 0000000000..7aaffbd823 --- /dev/null +++ b/Ghidra/Features/Base/src/test/resources/ghidra/app/util/cparser/zeroarray.h @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// TEMPORARY / TESTING - Zero-length Array Components +// - how should trailing zero-length arrays be handled/defined within structure since they may have an +// offset equal to the length of the structure (current flex array only allows for 1). The last component +// may not remain the last components as components are added. In addition, multiple trailing flex-arrays +// may exist. + +struct zeroArrayStruct1 { + int a; + char a1[2]; + char x[0]; + int y[0]; + int b; +}; + +struct zeroArrayStruct2 { + int a; + char a1[2]; + char x[0]; + int y[0]; +}; + +struct zeroArrayStruct3 { + int a; + zeroArrayStruct2 s; +}; + +union zeroArrayUnion1 { + int a; + char a1[2]; + char x[0]; + int y[0]; + int b; +}; + +union zeroArrayUnion2 { + int a; + char a1[2]; + char x[0]; + int y[0]; +}; + +union zeroArrayUnion3 { + int a; + zeroArrayStruct2 s; +}; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java index d276ed0150..1ec28f5f9c 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java @@ -260,6 +260,8 @@ public class EditStructureUtils { int endOfDataTypeInStruct = offset + dataTypeLength; int roomForData = structure.getLength() - endOfDataTypeInStruct; + + // FIXME: This will not worked for structures where packing is enabled - not sure how to handle // if structure isn't defined insert if (structure.isNotYetDefined()) { 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 b22a11e7b9..5728dd17c2 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,9 +17,11 @@ 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; +import docking.widgets.OptionDialog; import ghidra.app.decompiler.ClangFieldToken; import ghidra.app.decompiler.ClangToken; import ghidra.app.plugin.core.decompile.DecompilerActionContext; @@ -47,43 +49,6 @@ public class RetypeFieldAction extends AbstractDecompilerAction { setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK)); } - /** - * Return the index of the last component that would be overwritten by a new datatype, if we started overwriting - * with at a specific component. All components, except the first, must be undefined datatypes or we return -1 - * @param struct is the structure we are testing - * @param comp is the starting component to overwrite - * @param newtype is the datatype to overwrite with - * @return the index of the last component overwritten or -1 - */ - private static int getEndComponentIndex(Structure struct, DataTypeComponent comp, - DataType newtype) { - int newlen = newtype.getLength(); - if (newlen <= 0) { - return -1; // Don't support variable length types - } - DataType curtype = comp.getDataType(); - newlen -= curtype.getLength(); - int index = comp.getOrdinal(); - while (newlen > 0) { - index += 1; - if (index >= struct.getNumComponents()) { - return -1; // Not enough space in the structure - } - comp = struct.getComponent(index); -// String nm = comp.getFieldName(); -// if ((nm !=null)&&(nm.length()!=0)) -// return -1; - curtype = comp.getDataType(); -// if (!Undefined.isUndefined(curtype)) -// return -1; // Overlaps non-undefined datatype - if (curtype != DataType.DEFAULT) { - return -1; // Only allow overwrite of placeholder components - } - newlen -= curtype.getLength(); - } - return index; - } - @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { Function function = context.getFunction(); @@ -102,6 +67,21 @@ 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(); @@ -109,7 +89,6 @@ public class RetypeFieldAction extends AbstractDecompilerAction { ClangToken tokenAtCursor = context.getTokenAtCursor(); DataTypeManager dataTypeManager = program.getDataTypeManager(); - DataType dataType = null; Structure struct = getStructDataType(tokenAtCursor); int offset = ((ClangFieldToken) tokenAtCursor).getOffset(); if (struct == null) { @@ -119,64 +98,108 @@ public class RetypeFieldAction extends AbstractDecompilerAction { } if (offset < 0 || offset >= struct.getLength()) { Msg.showError(this, tool.getToolFrame(), "Retype Failed", - "Failed to re-type structure: " + struct.getName()); + "Failed to re-type structure field at offset " + offset + ": " + struct.getName()); return; } - DataTypeComponent comp = struct.getComponentAt(offset); - if (comp == null) { - dataType = chooseDataType(tool, program, DataType.DEFAULT); - } - else { - dataType = chooseDataType(tool, program, comp.getDataType()); - } + // Get original component and datatype + DataTypeComponent comp = getComponentContaining(struct, offset); + DataType originalDataType = comp != null ? comp.getDataType() : DataType.DEFAULT; + if (originalDataType instanceof BitFieldDataType) { + Msg.showError(this, tool.getToolFrame(), "Retype Failed", + "Retype of defind bit-field is not supported."); + return; + } + + DataType dataType = chooseDataType(tool, program, originalDataType); if (dataType == null) { - return; + return; // cancelled } - boolean successfulMod = false; - if (comp == null) { - if (!struct.isNotYetDefined()) { - Msg.showError(this, tool.getToolFrame(), "Retype Failed", - "Could not find component of '" + struct.getName() + "' to retype"); - return; - } - // note if we reach here the offset must be zero, so assume we are inserting newtype - int transaction = program.startTransaction("Retype Structure Field"); - try { - // Make sure datatype is using the program's data organization before testing fit - if (dataType.getDataTypeManager() != dataTypeManager) { - dataType = dataTypeManager.resolve(dataType, null); - } - struct.insert(0, dataType); - successfulMod = true; - } - finally { - program.endTransaction(transaction, successfulMod); - } - return; + + // check for permitted datatype + if (dataType instanceof FactoryDataType || dataType.getLength() <= 0) { + Msg.showError(this, tool.getToolFrame(), "Retype Failed", + "Retype field with \"" + dataType.getName() + "\" data type is not allowed."); } + int transaction = program.startTransaction("Retype Structure Field"); try { - // Make sure datatype is using the program's data organization before testing fit - if (dataType.getDataTypeManager() != dataTypeManager) { - dataType = dataTypeManager.resolve(dataType, null); + dataType = dataTypeManager.resolve(dataType, null); + int newDtLength = dataType.getLength(); + + if (DataTypeComponent.usesZeroLengthComponent(dataType)) { + Msg.showError(this, tool.getToolFrame(), "Retype Failed", "Retype field with \"" + + dataType.getName() + "\" zero-length component is not allowed."); } - int startind = comp.getOrdinal(); - int endind = getEndComponentIndex(struct, comp, dataType); - if (endind < 0) { - Msg.showError(this, tool.getToolFrame(), "Retype Failed", - "Failed to re-type structure '" + struct.getName() + "': Datatype did not fit"); + + if (originalDataType != DataType.DEFAULT && + newDtLength == originalDataType.getLength()) { + // Perform simple 1-for-1 component replacement - this allows to avoid unpack in some cases - assume comp is not null + struct.replace(comp.getOrdinal(), dataType, -1); return; } - for (int i = endind; i > startind; --i) { // Clear all but first field - struct.clearComponent(i); + + // check for datatype fit + String fieldName = null; + String comment = null; + int nextOffset; + if (comp == null) { + nextOffset = offset + 1; // assume padding offset within packed structure } - struct.replaceAtOffset(comp.getOffset(), dataType, dataType.getLength(), - comp.getFieldName(), comp.getComment()); - successfulMod = true; + else { + fieldName = comp.getFieldName(); + comment = comp.getComment(); + nextOffset = comp.getEndOffset() + 1; + } + int available = nextOffset - offset; + if (newDtLength > available) { + DataTypeComponent nextComp = struct.getDefinedComponentAtOrAfterOffset(nextOffset); + int endOffset = nextComp == null ? struct.getLength() : nextComp.getOffset(); + available += endOffset - nextOffset; + if (newDtLength > available) { + Msg.showError(this, tool.getToolFrame(), "Retype Failed", + "Failed to re-type structure '" + struct.getName() + + "': Datatype will not fit"); + return; + } + } + + if (struct.isPackingEnabled() && !isAlignmentMaintained(comp, dataType, offset)) { + int choice = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, + "Disable Structure Packing", + "Containing structure currently has packing enabled. Packing will be disabled if you continue.", + "Continue", OptionDialog.WARNING_MESSAGE); + if (choice != OptionDialog.OPTION_ONE) { + return; // cancelled + } + // alignment is maintained for struct since we do not know the extent of the impact if we change it + int alignment = struct.getAlignment(); + struct.setPackingEnabled(false); + struct.setExplicitMinimumAlignment(alignment); // preserve previously computed alignment + } + + // The replaceAtOffset will only replace component containing offset plus any subsequent DEFAULT + // components available. Space check is performed prior to any clearing. Zero-length components + // at offset will be ignored. + struct.replaceAtOffset(offset, dataType, -1, fieldName, comment); + } + catch (IllegalArgumentException e) { + Msg.showError(this, tool.getToolFrame(), "Retype Failed", + "Failed to re-type structure: " + e.getMessage()); } finally { - program.endTransaction(transaction, successfulMod); + program.endTransaction(transaction, true); } } + + private boolean isAlignmentMaintained(DataTypeComponent comp, DataType dataType, int offset) { + if (comp == null) { + return false; + } + int align = comp.getDataType().getAlignment(); + if (align != dataType.getAlignment()) { + return false; + } + return (offset % align) == 0; + } } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java index 7a8c7fcad0..f42ab061a7 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/util/DataTypeDependencyOrderer.java @@ -265,9 +265,6 @@ public class DataTypeDependencyOrderer { for (DataTypeComponent dtcomp : dtcomps) { addDependent(entry, dtcomp.getDataType()); } - if (struct.hasFlexibleArrayComponent()) { - addDependent(entry, struct.getFlexibleArrayComponent().getDataType()); - } } else if (dataType instanceof Composite) { DataTypeComponent dtcomps[] = ((Composite) dataType).getComponents(); diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java index 7de2248c3c..e24c3ef1d8 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java @@ -241,7 +241,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { // included in the length of the structure and must have a properly sized char array // created immediately following the structure in memory. - struct.setFlexibleArrayComponent(CharDataType.dataType, "name", null); + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "name", null); return MSDataTypeUtils.getMatchingDataType(program, struct); } @@ -281,8 +281,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { @Override protected int getDataTypeLength() { Structure struct = (Structure) getDataType(); - DataTypeComponent nameComponent = struct.getFlexibleArrayComponent(); - int preNameLength = nameComponent.getOffset(); + int preNameLength = struct.getLength(); int totalLength = preNameLength; // Add the length of the name string too if we can get it. Address nameAddress = getAddress().add(preNameLength); diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java index 20b51c99d6..1b1202556c 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java @@ -384,24 +384,24 @@ public class AbstractCreateDataTypeModelTest extends AbstractGenericTest { protected void CheckTypeDefOnStructureData(ProgramDB program, long address, String expectedName, String[] expectedFieldNames, int expectedDtLength) { - CheckStructureData(program, address, expectedName, expectedFieldNames, null, + CheckStructureData(program, address, expectedName, expectedFieldNames, false, expectedDtLength, true); } protected void CheckStructureData(ProgramDB program, long address, String expectedName, String[] expectedFieldNames, int expectedDtLength) { - CheckStructureData(program, address, expectedName, expectedFieldNames, null, + CheckStructureData(program, address, expectedName, expectedFieldNames, false, expectedDtLength, false); } protected void CheckStructureData(ProgramDB program, long address, String expectedName, - String[] expectedFieldNames, String flexArrayName, int expectedDtLength) { - CheckStructureData(program, address, expectedName, expectedFieldNames, flexArrayName, + String[] expectedFieldNames, boolean lastFieldIsFlexArray, int expectedDtLength) { + CheckStructureData(program, address, expectedName, expectedFieldNames, lastFieldIsFlexArray, expectedDtLength, false); } protected void CheckStructureData(ProgramDB program, long address, String expectedName, - String[] expectedFieldNames, String flexArrayName, int expectedDtLength, + String[] expectedFieldNames, boolean lastFieldIsFlexArray, int expectedDtLength, boolean isTypeDefOfStructure) { Listing listing = program.getListing(); Data data = listing.getDataAt(addr(program, address)); @@ -421,24 +421,22 @@ public class AbstractCreateDataTypeModelTest extends AbstractGenericTest { assertEquals("Mismatch in expected structure component count: " + name, expectedFieldNames.length, structure.getNumComponents()); DataTypeComponent[] components = structure.getComponents(); + boolean hasTrailingFlexArray = false; for (int i = 0; i < components.length; i++) { assertEquals( "Expected component " + i + " to be named " + expectedFieldNames[i] + " but was " + components[i].getFieldName(), expectedFieldNames[i], components[i].getFieldName()); + DataType dt = components[i].getDataType(); + hasTrailingFlexArray = (dt instanceof Array) && dt.isZeroLength(); } - if (flexArrayName != null) { - DataTypeComponent flexibleArrayComponent = structure.getFlexibleArrayComponent(); - assertNotNull("Structure does not contain flexible array: " + name, - flexibleArrayComponent); - assertEquals( - "Expected flexible array named " + flexArrayName + " but was " + - flexibleArrayComponent.getFieldName(), - flexArrayName, flexibleArrayComponent.getFieldName()); + + if (lastFieldIsFlexArray) { + assertTrue("Structure does not end with flexible array", hasTrailingFlexArray); } else { - assertFalse("Structure contains unexpected flexible array component: " + name, - structure.hasFlexibleArrayComponent()); + assertFalse("Structure contains unexpected flexible array component", + hasTrailingFlexArray); } } @@ -493,8 +491,9 @@ public class AbstractCreateDataTypeModelTest extends AbstractGenericTest { protected void checkTypeDescriptorData(ProgramDB program, long address, int structLength, int nameArrayLength, String expectedTypeName) { - CheckStructureData(program, address, "TypeDescriptor", new String[] { "pVFTable", "spare" }, - "name", structLength); + CheckStructureData(program, address, "TypeDescriptor", + new String[] { "pVFTable", "spare", "name" }, + true, structLength); checkTypeName(program, address, expectedTypeName); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java index 1aae0e185d..17fd82135a 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java @@ -234,8 +234,11 @@ public class DefaultCompositeMember extends CompositeMember { // transform last member into flexible array Structure struct = (Structure) memberDataType; Array array = (Array) m.getDataType(); - struct.setFlexibleArrayComponent(array.getDataType(), m.getName(), m.memberComment); // use unmodified comment - struct.delete(struct.getNumComponents() - 1); + // TODO: there may be a more direct approach since we now handle zero-length array instantiation + struct.delete(struct.getNumComponents() - 1); // delete placeholder component + struct.insertAtOffset(m.memberOffset, + new ArrayDataType(array.getDataType(), 0, 1, dataTypeManager), 0, m.getName(), + m.memberComment); // use unmodified comment } } @@ -286,7 +289,7 @@ public class DefaultCompositeMember extends CompositeMember { return; } - DataTypeComponent dtc = struct.getComponentAt(preferredSize); + DataTypeComponent dtc = struct.getComponentContaining(preferredSize); if (dtc == null) { return; } @@ -1010,7 +1013,7 @@ public class DefaultCompositeMember extends CompositeMember { } else if (isStructureContainer()) { Structure struct = (Structure) memberDataType; - // TODO: complicated by bitfields + // TODO: complicated by bitfields where multiple components may occupy same byte struct.deleteAtOffset(newContainerMember.getOffset()); struct.insertAtOffset(newContainerMember.getOffset(), newContainerMember.getDataType(), newContainerMember.getLength()); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java index 897cccf3ac..9d6345fa0f 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java @@ -1285,7 +1285,7 @@ public class CppCompositeType { } private int getCompositeLength(Composite myComposite) { - if (!myComposite.isNotYetDefined()) { + if (!myComposite.isZeroLength()) { return myComposite.getLength(); } return 0; diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java index 58094a4358..8fe4a8b13d 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java @@ -331,7 +331,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "pack()\n" + "Structure struct {\n" + " 0 char 1 a \"\"\n" + - " char[0] 0 e \"\"\n" + + " 1 char[0] 0 e \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1", struct, true); //@formatter:on @@ -371,14 +371,14 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest " 0 int 4 a \"\"\n" + " 4 int 4 b \"\"\n" + " 8 int 4 c \"\"\n" + - " char[0] 0 d \"\"\n" + + " 12 char[0] 0 d \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/union/union_s_1\n" + "pack()\n" + "Structure union_s_1 {\n" + " 0 longlong 8 e \"\"\n" + - " char[0] 0 f \"\"\n" + + " 8 char[0] 0 f \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 8\n", struct, true); //@formatter:on @@ -408,7 +408,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "pack()\n" + "Union union {\n" + " 0 union_s_0 12 _s_0 \"\"\n" + - " 0 union_s_1 1 _s_1 \"\"\n" + + " 0 union_s_1 0 _s_1 \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/union/union_s_0\n" + @@ -417,13 +417,13 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest " 0 int 4 a \"\"\n" + " 4 int 4 b \"\"\n" + " 8 int 4 c \"\"\n" + - " char[0] 0 d \"\"\n" + + " 12 char[0] 0 d \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/union/union_s_1\n" + "pack()\n" + "Structure union_s_1 {\n" + - " char[0] 0 f \"\"\n" + + " 0 char[0] 0 f \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n", struct, true); //@formatter:on @@ -459,7 +459,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "pack()\n" + "Structure union_s_0 {\n" + " 0 char 1 a \"\"\n" + - " char[0] 0 flex \"\"\n" + + " 1 char[0] 0 flex \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/union/union_s_1\n" + @@ -767,7 +767,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "pack()\n" + "Union MoreComplicated_s_u_44 {\n" + " 0 MoreComplicated_s_u_44_s_0 12 _s_0 \"\"\n" + - " 0 MoreComplicated_s_u_44_s_1 1 _s_1 \"\"\n" + + " 0 MoreComplicated_s_u_44_s_1 0 _s_1 \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_0\n" + @@ -776,13 +776,13 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest " 0 int 4 fromAddress \"\"\n" + " 4 int 4 toAddress \"\"\n" + " 8 int 4 seqNum \"\"\n" + - " char[0] 0 data \"\"\n" + + " 12 char[0] 0 data \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_1\n" + "pack()\n" + "Structure MoreComplicated_s_u_44_s_1 {\n" + - " char[0] 0 buf \"\"\n" + + " 0 char[0] 0 buf \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/MoreComplicated_s/MoreComplicated_s_u_8\n" + diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/BooleanSettingsDefinition.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/BooleanSettingsDefinition.java index a92391df6e..87d65f6d23 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/BooleanSettingsDefinition.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/BooleanSettingsDefinition.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.docking.settings; /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java index 6b5fa1654f..e53b99a623 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java @@ -283,9 +283,15 @@ public class PseudoData extends PseudoCodeUnit implements Data { return (component == null ? null : component.getComponent(componentPath)); } + @Deprecated @Override public Data getComponentAt(int offset) { - if (offset < 0 || offset >= length) { + return getComponentContaining(offset); + } + + @Override + public Data getComponentContaining(int offset) { + if (offset < 0 || offset > length) { return null; } @@ -294,21 +300,20 @@ public class PseudoData extends PseudoCodeUnit implements Data { int elementLength = array.getElementLength(); int index = offset / elementLength; return getComponent(index); - - } - else if (baseDataType instanceof Union) { - return getComponent(0); } else if (baseDataType instanceof Structure) { Structure struct = (Structure) baseDataType; - DataTypeComponent dtc = struct.getComponentAt(offset); - return getComponent(dtc.getOrdinal()); - + DataTypeComponent dtc = struct.getComponentContaining(offset); + return (dtc != null) ? getComponent(dtc.getOrdinal()) : null; } else if (baseDataType instanceof DynamicDataType) { DynamicDataType ddt = (DynamicDataType) baseDataType; DataTypeComponent dtc = ddt.getComponentAt(offset, this); - return getComponent(dtc.getOrdinal()); + return (dtc != null) ? getComponent(dtc.getOrdinal()) : null; + } + else if (baseDataType instanceof Union) { + // TODO: Returning anything is potentially bad + //return getComponent(0); } return null; } @@ -328,14 +333,8 @@ public class PseudoData extends PseudoCodeUnit implements Data { } else if (baseDataType instanceof Structure) { Structure struct = (Structure) baseDataType; - DataTypeComponent dtc = struct.getComponentAt(offset); - // Logic handles overlapping bit-fields - // Include if offset is contained within bounds of component - while (dtc != null && (offset >= dtc.getOffset()) && - (offset <= (dtc.getOffset() + dtc.getLength() - 1))) { - int ordinal = dtc.getOrdinal(); - list.add(getComponent(ordinal++)); - dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null; + for (DataTypeComponent dtc : struct.getComponentsContaining(offset)) { + list.add(getComponent(dtc.getOrdinal())); } } else if (baseDataType instanceof DynamicDataType) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDataComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDataComponent.java index 35ebf86e69..fca1c1b021 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDataComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDataComponent.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.app.util; import ghidra.docking.settings.Settings; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java index d05e427c40..899eeb962a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database; import java.lang.ref.ReferenceQueue; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java index 6f7982bd1d..4ebf86be71 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java @@ -45,9 +45,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB * database schema associated with any of the managers. * 18-Sep-2008 - version 1 - added fields for synchronizing program data types with project archives. * 03-Dec-2009 - version 2 - Added source archive updating (consolidating windows.gdt, clib.gdt, ntddk.gdt) - * 14-Nov-2019 - version 3 - Corrected fixed length indexing implementation causing - * change in index table low-level storage for newly - * created tables. + * 14-Nov-2019 - version 3 - Corrected fixed length indexing implementation causing change + * in index table low-level storage for newly created tables. */ static final int DB_VERSION = 3; @@ -435,6 +434,7 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB } private void upgradeDatabase() throws IOException { + table = dbh.getTable(TABLE_NAME); DBRecord record = SCHEMA.createRecord(new StringField(ARCHIVE_DB_VERSION)); record.setString(0, Integer.toString(DB_VERSION)); @@ -503,7 +503,8 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB return versionExc; } - private void initManagers(int openMode, TaskMonitor monitor) throws CancelledException { + private void initManagers(int openMode, TaskMonitor monitor) + throws IOException, CancelledException { monitor.checkCanceled(); dataTypeManager.setDataTypeArchive(this); dataTypeManager.archiveReady(openMode, monitor); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java index e9693cf110..eda50b76f9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java @@ -98,7 +98,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM * created tables. * 18-Feb-2021 - version 23 Added support for Big Reflist for tracking FROM references. * Primarily used for large numbers of Entry Point references. - * 31-Mar-2021 - version 24 Added support for CompilerSpec extensions + * 31-Mar-2021 - version 24 Added support for CompilerSpec extensions */ static final int DB_VERSION = 24; @@ -2060,7 +2060,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM Msg.info(this, "Updating language version for Program " + getName() + ": " + language.getLanguageDescription() + " (Version " + - language.getVersion() + ")"); + language.getVersion() + "." + language.getMinorVersion()); } if (newCompilerSpecID != null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java index 8999a62d11..b04b3de2d9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java @@ -284,7 +284,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC public Address getMaxAddress() { refreshIfNeeded(); if (endAddr == null) { - endAddr = address.add(length - 1); + endAddr = length == 0 ? address : address.add(length - 1); } return endAddr; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataComponent.java index 0a2f33c204..67634eec4d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataComponent.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.code; import db.DBRecord; @@ -154,14 +151,6 @@ class DataComponent extends DataDB { return path; } - /** - * @see ghidra.program.model.listing.CodeUnit#getLength() - */ - @Override - public int getLength() { - return length; - } - /** * Get the name of this Data that is a component of another * Data Item. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java index da27e2690e..ed11306b27 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java @@ -15,8 +15,7 @@ */ package ghidra.program.database.code; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import db.DBRecord; import ghidra.docking.settings.Settings; @@ -173,9 +172,6 @@ class DataDB extends CodeUnitDB implements Data { bytes = null; } - /** - * @see ghidra.program.model.listing.Data#addValueReference(ghidra.program.model.address.Address, ghidra.program.model.symbol.RefType) - */ @Override public void addValueReference(Address refAddr, RefType type) { refreshIfNeeded(); @@ -183,17 +179,11 @@ class DataDB extends CodeUnitDB implements Data { CodeManager.DATA_OP_INDEX); } - /** - * @see ghidra.program.model.listing.Data#removeValueReference(ghidra.program.model.address.Address) - */ @Override public void removeValueReference(Address refAddr) { removeOperandReference(CodeManager.DATA_OP_INDEX, refAddr); } - /** - * @see ghidra.program.model.listing.Data#getComponent(int) - */ @Override public Data getComponent(int index) { lock.acquire(); @@ -249,9 +239,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.CodeUnit#getAddress(int) - */ @Override public Address getAddress(int opIndex) { if (opIndex == 0) { @@ -277,9 +264,6 @@ class DataDB extends CodeUnitDB implements Data { return mnemonicString + " " + valueRepresentation; } - /** - * @see ghidra.program.model.listing.Data#getDefaultValueRepresentation() - */ @Override public String getDefaultValueRepresentation() { lock.acquire(); @@ -292,9 +276,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.CodeUnit#getMnemonicString() - */ @Override public String getMnemonicString() { lock.acquire(); @@ -307,17 +288,11 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.CodeUnit#getNumOperands() - */ @Override public int getNumOperands() { return 1; } - /** - * @see ghidra.program.model.listing.CodeUnit#getScalar(int) - */ @Override public Scalar getScalar(int opIndex) { if (opIndex == 0) { @@ -334,9 +309,6 @@ class DataDB extends CodeUnitDB implements Data { return null; } - /** - * @see ghidra.program.model.listing.Data#getBaseDataType() - */ @Override public DataType getBaseDataType() { return baseDataType; @@ -375,25 +347,16 @@ class DataDB extends CodeUnitDB implements Data { return false; } - /** - * @see ghidra.program.model.listing.Data#isConstant() - */ @Override public boolean isConstant() { return hasMutability(MutabilitySettingsDefinition.CONSTANT); } - /** - * @see ghidra.program.model.listing.Data#isVolatile() - */ @Override public boolean isVolatile() { return hasMutability(MutabilitySettingsDefinition.VOLATILE); } - /** - * @see ghidra.docking.settings.Settings#clear(java.lang.String) - */ @Override public void clearSetting(String name) { refreshIfNeeded(); @@ -404,9 +367,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.docking.settings.Settings#getByteArray(java.lang.String) - */ @Override public byte[] getByteArray(String name) { refreshIfNeeded(); @@ -417,9 +377,6 @@ class DataDB extends CodeUnitDB implements Data { return tempBytes; } - /** - * @see ghidra.docking.settings.Settings#getLong(java.lang.String) - */ @Override public Long getLong(String name) { refreshIfNeeded(); @@ -430,18 +387,12 @@ class DataDB extends CodeUnitDB implements Data { return value; } - /** - * @see ghidra.docking.settings.Settings#getNames() - */ @Override public String[] getNames() { refreshIfNeeded(); return dataMgr.getNames(getDataSettingsAddress()); } - /** - * @see ghidra.docking.settings.Settings#getString(java.lang.String) - */ @Override public String getString(String name) { refreshIfNeeded(); @@ -452,9 +403,6 @@ class DataDB extends CodeUnitDB implements Data { return value; } - /** - * @see ghidra.docking.settings.Settings#getValue(java.lang.String) - */ @Override public Object getValue(String name) { refreshIfNeeded(); @@ -465,9 +413,6 @@ class DataDB extends CodeUnitDB implements Data { return value; } - /** - * @see ghidra.docking.settings.Settings#setByteArray(java.lang.String, byte[]) - */ @Override public void setByteArray(String name, byte[] value) { refreshIfNeeded(); @@ -478,9 +423,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.docking.settings.Settings#setLong(java.lang.String, long) - */ @Override public void setLong(String name, long value) { refreshIfNeeded(); @@ -491,9 +433,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.docking.settings.Settings#setString(java.lang.String, java.lang.String) - */ @Override public void setString(String name, String value) { refreshIfNeeded(); @@ -504,9 +443,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.docking.settings.Settings#setValue(java.lang.String, java.lang.Object) - */ @Override public void setValue(String name, Object value) { refreshIfNeeded(); @@ -517,9 +453,6 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.Data#getComponent(int[]) - */ @Override public Data getComponent(int[] componentPath) { lock.acquire(); @@ -537,7 +470,7 @@ class DataDB extends CodeUnitDB implements Data { @Override public String getComment(int commentType) { - Data child = getComponentAt(0); + Data child = getComponentContaining(0); if (child != null) { // avoid caching issue by maintaining comment at lowest point in data path return child.getComment(commentType); @@ -547,7 +480,7 @@ class DataDB extends CodeUnitDB implements Data { @Override public void setComment(int commentType, String comment) { - Data child = getComponentAt(0); + Data child = getComponentContaining(0); if (child != null) { // avoid caching issue by maintaining comment at lowest point in data path child.setComment(commentType, comment); @@ -557,12 +490,18 @@ class DataDB extends CodeUnitDB implements Data { } } + @Deprecated @Override public Data getComponentAt(int offset) { + return getComponentContaining(offset); + } + + @Override + public Data getComponentContaining(int offset) { lock.acquire(); try { checkIsValid(); - if (offset < 0 || offset >= length) { + if (offset < 0 || offset > length) { return null; } @@ -574,7 +513,7 @@ class DataDB extends CodeUnitDB implements Data { } else if (baseDataType instanceof Structure) { Structure struct = (Structure) baseDataType; - DataTypeComponent dtc = struct.getComponentAt(offset); + DataTypeComponent dtc = struct.getComponentContaining(offset); return (dtc != null) ? getComponent(dtc.getOrdinal()) : null; } else if (baseDataType instanceof DynamicDataType) { @@ -596,53 +535,52 @@ class DataDB extends CodeUnitDB implements Data { @Override public List getComponentsContaining(int offset) { - List list = new ArrayList<>(); lock.acquire(); try { checkIsValid(); if (offset < 0 || offset >= length) { return null; } - if (baseDataType instanceof Array) { Array array = (Array) baseDataType; int elementLength = array.getElementLength(); int index = offset / elementLength; - list.add(getComponent(index)); + return Collections.singletonList(getComponent(index)); } else if (baseDataType instanceof Structure) { Structure struct = (Structure) baseDataType; - DataTypeComponent dtc = struct.getComponentAt(offset); - // Logic handles overlapping bit-fields - // Include if offset is contained within bounds of component - while (dtc != null && (offset >= dtc.getOffset()) && - (offset <= (dtc.getOffset() + dtc.getLength() - 1))) { - int ordinal = dtc.getOrdinal(); - list.add(getComponent(ordinal++)); - dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null; + List result = new ArrayList<>(); + for (DataTypeComponent dtc : struct.getComponentsContaining(offset)) { + result.add(getComponent(dtc.getOrdinal())); } + return result; } else if (baseDataType instanceof DynamicDataType) { DynamicDataType ddt = (DynamicDataType) baseDataType; DataTypeComponent dtc = ddt.getComponentAt(offset, this); + List result = new ArrayList<>(); // Logic handles overlapping bit-fields // Include if offset is contained within bounds of component while (dtc != null && (offset >= dtc.getOffset()) && - (offset <= (dtc.getOffset() + dtc.getLength() - 1))) { + (offset < (dtc.getOffset() + dtc.getLength()))) { int ordinal = dtc.getOrdinal(); - list.add(getComponent(ordinal++)); + result.add(getComponent(ordinal++)); dtc = ordinal < ddt.getNumComponents(this) ? ddt.getComponent(ordinal, this) : null; } + return result; } else if (baseDataType instanceof Union) { - if (offset == 0) { - for (int i = 0; i < getNumComponents(); i++) { - list.add(getComponent(i)); + Union union = (Union) baseDataType; + List result = new ArrayList<>(); + for (DataTypeComponent dtc : union.getComponents()) { + if (offset < dtc.getLength()) { + result.add(getComponent(dtc.getOrdinal())); } } + return result; } - return list; + return Collections.emptyList(); } finally { lock.release(); @@ -650,41 +588,26 @@ class DataDB extends CodeUnitDB implements Data { } - /** - * @see ghidra.program.model.listing.Data#getComponentIndex() - */ @Override public int getComponentIndex() { return -1; } - /** - * @see ghidra.program.model.listing.Data#getComponentLevel() - */ @Override public int getComponentLevel() { return level; } - /** - * @see ghidra.program.model.listing.Data#getComponentPath() - */ @Override public int[] getComponentPath() { return EMPTY_PATH; } - /** - * @see ghidra.program.model.listing.Data#getComponentPathName() - */ @Override public String getComponentPathName() { return null; } -// /** -// * @see ghidra.program.model.listing.Data#getComponents() -// */ // public Data[] getComponents() { // lock.acquire(); // try { @@ -724,25 +647,16 @@ class DataDB extends CodeUnitDB implements Data { // } // } - /** - * @see ghidra.program.model.listing.Data#getDataType() - */ @Override public DataType getDataType() { return dataType; } - /** - * @see ghidra.program.model.listing.Data#getFieldName() - */ @Override public String getFieldName() { return null; } - /** - * @see ghidra.program.model.listing.Data#getNumComponents() - */ @Override public int getNumComponents() { lock.acquire(); @@ -774,25 +688,16 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.Data#getParent() - */ @Override public Data getParent() { return null; } - /** - * @see ghidra.program.model.listing.Data#getParentOffset() - */ @Override public int getParentOffset() { return 0; } - /** - * @see ghidra.program.model.listing.Data#getPathName() - */ @Override public String getPathName() { refreshIfNeeded(); @@ -805,9 +710,6 @@ class DataDB extends CodeUnitDB implements Data { return symbol.getName(); } - /** - * @see ghidra.program.model.listing.Data#getPrimitiveAt(int) - */ @Override public Data getPrimitiveAt(int offset) { lock.acquire(); @@ -816,7 +718,7 @@ class DataDB extends CodeUnitDB implements Data { if (offset < 0 || offset >= length) { return null; } - Data dc = getComponentAt(offset); + Data dc = getComponentContaining(offset); if (dc == null || dc == this) { return this; } @@ -827,25 +729,16 @@ class DataDB extends CodeUnitDB implements Data { } } - /** - * @see ghidra.program.model.listing.Data#getRoot() - */ @Override public Data getRoot() { return this; } - /** - * @see ghidra.program.model.listing.Data#getRootOffset() - */ @Override public int getRootOffset() { return 0; } - /** - * @see ghidra.program.model.listing.Data#getValue() - */ @Override public Object getValue() { lock.acquire(); @@ -883,65 +776,41 @@ class DataDB extends CodeUnitDB implements Data { return dataType.getDefaultLabelPrefix(this, this, length, options); } - /** - * @see ghidra.program.model.listing.Data#getValueReferences() - */ @Override public Reference[] getValueReferences() { return getOperandReferences(CodeManager.DATA_OP_INDEX); } - /** - * @see ghidra.program.model.listing.Data#isArray() - */ @Override public boolean isArray() { return baseDataType instanceof Array; } - /** - * @see ghidra.program.model.listing.Data#isDefined() - */ @Override public boolean isDefined() { return !(dataType instanceof DefaultDataType); } - /** - * @see ghidra.program.model.listing.Data#isPointer() - */ @Override public boolean isPointer() { return baseDataType instanceof Pointer; } - /** - * @see ghidra.program.model.listing.Data#isStructure() - */ @Override public boolean isStructure() { return baseDataType instanceof Structure; } - /** - * @see ghidra.program.model.listing.Data#isDynamic() - */ @Override public boolean isDynamic() { return baseDataType instanceof DynamicDataType; } - /** - * @see ghidra.program.model.listing.Data#isUnion() - */ @Override public boolean isUnion() { return baseDataType instanceof Union; } - /** - * @see ghidra.docking.settings.Settings#clearAllSettings() - */ @Override public void clearAllSettings() { refreshIfNeeded(); @@ -951,18 +820,12 @@ class DataDB extends CodeUnitDB implements Data { null); } - /** - * @see ghidra.docking.settings.Settings#isEmpty() - */ @Override public boolean isEmpty() { refreshIfNeeded(); return dataMgr.isEmptySetting(getDataSettingsAddress()); } - /** - * @see ghidra.program.model.listing.CodeUnit#getReferencesFrom() - */ @Override public Reference[] getReferencesFrom() { ArrayList list = new ArrayList<>(); @@ -979,9 +842,6 @@ class DataDB extends CodeUnitDB implements Data { return list.toArray(new Reference[list.size()]); } - /** - * @see ghidra.docking.settings.Settings#getDefaultSettings() - */ @Override public Settings getDefaultSettings() { return defaultSettings; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java index 4230202da8..3b514cbfbf 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.code; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapterV1.java index bc76b75afc..10b6459662 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapterV1.java @@ -13,21 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.code; -import ghidra.program.database.map.*; -import ghidra.program.database.util.DatabaseTableUtils; -import ghidra.program.model.address.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.program.database.map.*; +import ghidra.program.database.util.DatabaseTableUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Version 0 adapter for the instruction table. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java index 9795c5189e..c7fa04a507 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java @@ -117,12 +117,15 @@ class ArrayDB extends DataTypeDB implements Array { @Override public boolean isZeroLength() { - return getDataType().isZeroLength(); + return getNumElements() == 0; } @Override public int getLength() { - checkIsValid(); + validate(lock); + if (getNumElements() == 0) { + return 1; // 0-length datatype instance not supported + } return getNumElements() * getElementLength(); } @@ -159,19 +162,17 @@ class ArrayDB extends DataTypeDB implements Array { DataType dt = getDataType(); int elementLen; if (dt instanceof Dynamic) { - elementLen = record.getIntValue(ArrayDBAdapter.ARRAY_LENGTH_COL); + elementLen = record.getIntValue(ArrayDBAdapter.ARRAY_ELEMENT_LENGTH_COL); } else { elementLen = dt.getLength(); } - if (elementLen <= 0) { - elementLen = 1; - } return elementLen; } @Override public int getNumElements() { + validate(lock); return record.getIntValue(ArrayDBAdapter.ARRAY_DIM_COL); } @@ -221,23 +222,27 @@ class ArrayDB extends DataTypeDB implements Array { public void dataTypeReplaced(DataType oldDt, DataType newDt) { lock.acquire(); try { - String myOldName = getOldName(); checkIsValid(); if (newDt == this || newDt.getLength() < 0) { newDt = DataType.DEFAULT; } if (oldDt == getDataType()) { + oldDt.removeParent(this); newDt.addParent(this); + String myOldName = getOldName(); int oldLength = getLength(); + int oldAlignment = getAlignment(); + int oldElementLength = getElementLength(); + record.setLongValue(ArrayDBAdapter.ARRAY_DT_ID_COL, dataMgr.getResolvedID(newDt)); if (newDt instanceof Dynamic || newDt instanceof FactoryDataType) { newDt = DataType.DEFAULT; } - // can only handle fixed-length replacements - record.setIntValue(ArrayDBAdapter.ARRAY_LENGTH_COL, -1); + int elementLength = newDt.getLength() < 0 ? oldElementLength : -1; + record.setIntValue(ArrayDBAdapter.ARRAY_ELEMENT_LENGTH_COL, elementLength); try { adapter.updateRecord(record); } @@ -248,9 +253,12 @@ class ArrayDB extends DataTypeDB implements Array { if (!getName().equals(myOldName)) { notifyNameChanged(myOldName); } - if (getLength() != oldLength) { + if (getLength() != oldLength || oldElementLength != getElementLength()) { notifySizeChanged(false); } + else if (getAlignment() != oldAlignment) { + notifyAlignmentChanged(false); + } else { dataMgr.dataTypeChanged(this, false); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java index 5f82d3212f..ba42612d9e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java @@ -30,7 +30,7 @@ abstract class ArrayDBAdapter { static final Schema SCHEMA = ArrayDBAdapterV1.V1_SCHEMA; static final int ARRAY_DT_ID_COL = ArrayDBAdapterV1.V1_ARRAY_DT_ID_COL; static final int ARRAY_DIM_COL = ArrayDBAdapterV1.V1_ARRAY_DIM_COL; - static final int ARRAY_LENGTH_COL = ArrayDBAdapterV1.V1_ARRAY_LENGTH_COL; + static final int ARRAY_ELEMENT_LENGTH_COL = ArrayDBAdapterV1.V1_ARRAY_ELEMENT_LENGTH_COL; static final int ARRAY_CAT_COL = ArrayDBAdapterV1.V1_ARRAY_CAT_COL; static ArrayDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java index 1068127168..8e6c3e10e5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java @@ -27,7 +27,7 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter { private static final String ARRAY_TABLE_NAME = "Arrays"; private static final int V0_ARRAY_DT_ID_COL = 0; private static final int V0_ARRAY_DIM_COL = 1; - private static final int V0_ARRAY_LENGTH_COL = 2; + private static final int V0_ARRAY_ELEMENT_LENGTH_COL = 2; // applies to sizable dynamic types only private Table table; @@ -87,7 +87,7 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter { DBRecord rec = ArrayDBAdapter.SCHEMA.createRecord(oldRec.getKey()); rec.setLongValue(ArrayDBAdapter.ARRAY_DT_ID_COL, oldRec.getLongValue(V0_ARRAY_DT_ID_COL)); rec.setIntValue(ArrayDBAdapter.ARRAY_DIM_COL, oldRec.getIntValue(V0_ARRAY_DIM_COL)); - rec.setIntValue(ArrayDBAdapter.ARRAY_LENGTH_COL, oldRec.getIntValue(V0_ARRAY_LENGTH_COL)); + rec.setIntValue(ArrayDBAdapter.ARRAY_ELEMENT_LENGTH_COL, oldRec.getIntValue(V0_ARRAY_ELEMENT_LENGTH_COL)); rec.setLongValue(ArrayDBAdapter.ARRAY_CAT_COL, 0); return rec; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java index 92e734bd58..c1ec6c3779 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java @@ -32,7 +32,7 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { static final String ARRAY_TABLE_NAME = "Arrays"; static final int V1_ARRAY_DT_ID_COL = 0; static final int V1_ARRAY_DIM_COL = 1; - static final int V1_ARRAY_LENGTH_COL = 2; + static final int V1_ARRAY_ELEMENT_LENGTH_COL = 2; // applies to sizable dynamic types only static final int V1_ARRAY_CAT_COL = 3; private Table table; @@ -76,7 +76,7 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { DBRecord record = V1_SCHEMA.createRecord(key); record.setLongValue(V1_ARRAY_DT_ID_COL, dataTypeID); record.setIntValue(V1_ARRAY_DIM_COL, numberOfElements); - record.setIntValue(V1_ARRAY_LENGTH_COL, length); + record.setIntValue(V1_ARRAY_ELEMENT_LENGTH_COL, length); record.setLongValue(V1_ARRAY_CAT_COL, catID); table.putRecord(record); return record; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java index 7f6333879a..9075a5c7f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java index eef84c63b8..1cf7867ef7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java index cb10e688ef..62f260be8c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java index 0a437d1dc5..a1a7eef4aa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java @@ -71,6 +71,9 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal { * @return preferred component length */ protected int getPreferredComponentLength(DataType dataType, int length) { + if (DataTypeComponent.usesZeroLengthComponent(dataType)) { + return 0; + } if ((isPackingEnabled() || (this instanceof Union)) && !(dataType instanceof Dynamic)) { length = -1; // force use of datatype size } @@ -87,7 +90,7 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal { } return length; } - + @Override protected String doGetName() { return record.getString(CompositeDBAdapter.COMPOSITE_NAME_COL); @@ -191,6 +194,22 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal { @Override public abstract boolean hasLanguageDependantLength(); + /** + * Determine if this composite should be treated as undefined. + *

+ * A composite is considered undefined with a zero-length when it has + * no components and packing is disabled. A {@link DataTypeComponent} defined by an + * an datatype which is not-yet-defined (i.e., {@link DataType#isNotYetDefined()} is true) + * will always have a size of 1. If an empty composite should be treated as + * fully specified, packing on the composite should be enabled to ensure that + * a zero-length component is used should the occassion arise (e.g., empty structure + * placed within union as a component). + */ + @Override + public final boolean isNotYetDefined() { + return getNumComponents() == 0 && !isPackingEnabled(); + } + @Override public Object getValue(MemBuffer buf, Settings settings, int length) { return null; @@ -661,7 +680,7 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal { @Override public String toString() { - return CompositeDataTypeImpl.toString(this); + return CompositeInternal.toString(this); } /** @@ -670,6 +689,8 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal { * specification which may be influenced by the data organization. * If this composite changes parents will not be * notified - handling this is the caller's responsibility. + * It is assumed that this method is invoked on composites + * in dependency order. * @throws IOException if database IO error occurs */ protected abstract void fixupComponents() throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java index de084a5d83..8073e7f4be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java @@ -30,30 +30,43 @@ import ghidra.util.task.TaskMonitor; abstract class CompositeDBAdapter { static final String COMPOSITE_TABLE_NAME = "Composite Data Types"; - static final Schema COMPOSITE_SCHEMA = CompositeDBAdapterV5.V5_COMPOSITE_SCHEMA; + static final Schema COMPOSITE_SCHEMA = CompositeDBAdapterV5V6.V5V6_COMPOSITE_SCHEMA; - static final int COMPOSITE_NAME_COL = CompositeDBAdapterV5.V5_COMPOSITE_NAME_COL; - static final int COMPOSITE_COMMENT_COL = CompositeDBAdapterV5.V5_COMPOSITE_COMMENT_COL; - static final int COMPOSITE_IS_UNION_COL = CompositeDBAdapterV5.V5_COMPOSITE_IS_UNION_COL; - static final int COMPOSITE_CAT_COL = CompositeDBAdapterV5.V5_COMPOSITE_CAT_COL; - static final int COMPOSITE_LENGTH_COL = CompositeDBAdapterV5.V5_COMPOSITE_LENGTH_COL; - static final int COMPOSITE_ALIGNMENT_COL = CompositeDBAdapterV5.V5_COMPOSITE_ALIGNMENT_COL; + static final int COMPOSITE_NAME_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_NAME_COL; + static final int COMPOSITE_COMMENT_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_COMMENT_COL; + static final int COMPOSITE_IS_UNION_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_IS_UNION_COL; + static final int COMPOSITE_CAT_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_CAT_COL; + static final int COMPOSITE_LENGTH_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_LENGTH_COL; + static final int COMPOSITE_ALIGNMENT_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_ALIGNMENT_COL; static final int COMPOSITE_NUM_COMPONENTS_COL = - CompositeDBAdapterV5.V5_COMPOSITE_NUM_COMPONENTS_COL; + CompositeDBAdapterV5V6.V5V6_COMPOSITE_NUM_COMPONENTS_COL; static final int COMPOSITE_SOURCE_ARCHIVE_ID_COL = - CompositeDBAdapterV5.V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL; + CompositeDBAdapterV5V6.V5V6_COMPOSITE_SOURCE_ARCHIVE_ID_COL; static final int COMPOSITE_UNIVERSAL_DT_ID = - CompositeDBAdapterV5.V5_COMPOSITE_UNIVERSAL_DT_ID_COL; + CompositeDBAdapterV5V6.V5V6_COMPOSITE_UNIVERSAL_DT_ID_COL; static final int COMPOSITE_SOURCE_SYNC_TIME_COL = - CompositeDBAdapterV5.V5_COMPOSITE_SOURCE_SYNC_TIME_COL; + CompositeDBAdapterV5V6.V5V6_COMPOSITE_SOURCE_SYNC_TIME_COL; static final int COMPOSITE_LAST_CHANGE_TIME_COL = - CompositeDBAdapterV5.V5_COMPOSITE_LAST_CHANGE_TIME_COL; + CompositeDBAdapterV5V6.V5V6_COMPOSITE_LAST_CHANGE_TIME_COL; static final int COMPOSITE_PACKING_COL = - CompositeDBAdapterV5.V5_COMPOSITE_PACK_COL; - static final int COMPOSITE_MIN_ALIGN_COL = CompositeDBAdapterV5.V5_COMPOSITE_MIN_ALIGN_COL; + CompositeDBAdapterV5V6.V5V6_COMPOSITE_PACK_COL; + static final int COMPOSITE_MIN_ALIGN_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_MIN_ALIGN_COL; // Stored Packing and Minimum Alignment values are consistent with CompositeInternal + static final int FLEX_ARRAY_ELIMINATION_SCHEMA_VERSION = 6; + + private boolean flexArrayMigrationRequired = false; // signals flex-array migration required + + /** + * After construction for UPGRADE mode this method can be used to determine if + * a migration of all Struture flex-array components is required. + * @return true if flex-array migration required, else false + */ + final boolean isFlexArrayMigrationRequired() { + return flexArrayMigrationRequired; + } + /** * Gets an adapter for working with the composite data type database table. * The composite table is used to store structures and unions. The adapter is based @@ -68,11 +81,8 @@ abstract class CompositeDBAdapter { */ static CompositeDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) throws VersionException, IOException, CancelledException { - if (openMode == DBConstants.CREATE) { - return new CompositeDBAdapterV5(handle, true); - } try { - return new CompositeDBAdapterV5(handle, false); + return new CompositeDBAdapterV5V6(handle, openMode); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -95,6 +105,12 @@ abstract class CompositeDBAdapter { */ static CompositeDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException, IOException { + try { + return new CompositeDBAdapterV5V6(handle, DBConstants.READ_ONLY); + } + catch (VersionException e) { + // ignore + } try { return new CompositeDBAdapterV2V4(handle); } @@ -128,7 +144,7 @@ abstract class CompositeDBAdapter { long id = tmpHandle.startTransaction(); CompositeDBAdapter tmpAdapter = null; try { - tmpAdapter = new CompositeDBAdapterV5(tmpHandle, true); + tmpAdapter = new CompositeDBAdapterV5V6(tmpHandle, DBConstants.CREATE); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { monitor.checkCanceled(); @@ -136,7 +152,10 @@ abstract class CompositeDBAdapter { tmpAdapter.updateRecord(rec, false); } oldAdapter.deleteTable(handle); - CompositeDBAdapter newAdapter = new CompositeDBAdapterV5(handle, true); + CompositeDBAdapter newAdapter = new CompositeDBAdapterV5V6(handle, DBConstants.CREATE); + if (oldAdapter.getVersion() < FLEX_ARRAY_ELIMINATION_SCHEMA_VERSION) { + newAdapter.flexArrayMigrationRequired = true; + } it = tmpAdapter.getRecords(); while (it.hasNext()) { monitor.checkCanceled(); @@ -151,6 +170,12 @@ abstract class CompositeDBAdapter { } } + /** + * Get the adapter schema version + * @return adapter schema version + */ + abstract int getVersion(); + /** * Creates a database record for a composite data type (structure or union). * @param name the unique name for this data type @@ -240,4 +265,10 @@ abstract class CompositeDBAdapter { abstract DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException; + /** + * Get the number of composite records + * @return total number of composite records + */ + abstract int getRecordCount(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java index c5bc3f7d13..3ed7feb480 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java @@ -66,6 +66,15 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } } + int getVersion() { + return compositeTable.getSchema().getVersion(); + } + + @Override + int getRecordCount() { + return compositeTable.getRecordCount(); + } + @Override public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, @@ -138,5 +147,4 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { return null; } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java index 6f59f436aa..71a367b6f2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java @@ -71,6 +71,15 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } } + int getVersion() { + return compositeTable.getSchema().getVersion(); + } + + @Override + int getRecordCount() { + return compositeTable.getRecordCount(); + } + @Override public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java index c3a3180179..afc02c2a11 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java @@ -38,20 +38,20 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla static final int VERSION = 4; static final int MIN_READ_ONLY_VERSION = 2; - static final int V2_COMPOSITE_NAME_COL = 0; - static final int V2_COMPOSITE_COMMENT_COL = 1; - static final int V2_COMPOSITE_IS_UNION_COL = 2; - static final int V2_COMPOSITE_CAT_COL = 3; - static final int V2_COMPOSITE_LENGTH_COL = 4; - static final int V2_COMPOSITE_NUM_COMPONENTS_COL = 5; - static final int V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL = 6; - static final int V2_COMPOSITE_UNIVERSAL_DT_ID_COL = 7; - static final int V2_COMPOSITE_SOURCE_SYNC_TIME_COL = 8; - static final int V2_COMPOSITE_LAST_CHANGE_TIME_COL = 9; - static final int V2_COMPOSITE_PACK_COL = 10; // renamed from Internal Alignment - static final int V2_COMPOSITE_MIN_ALIGN_COL = 11; // renamed from External Alignment + static final int V2V4_COMPOSITE_NAME_COL = 0; + static final int V2V4_COMPOSITE_COMMENT_COL = 1; + static final int V2V4_COMPOSITE_IS_UNION_COL = 2; + static final int V2V4_COMPOSITE_CAT_COL = 3; + static final int V2V4_COMPOSITE_LENGTH_COL = 4; + static final int V2V4_COMPOSITE_NUM_COMPONENTS_COL = 5; + static final int V2V4_COMPOSITE_SOURCE_ARCHIVE_ID_COL = 6; + static final int V2V4_COMPOSITE_UNIVERSAL_DT_ID_COL = 7; + static final int V2V4_COMPOSITE_SOURCE_SYNC_TIME_COL = 8; + static final int V2V4_COMPOSITE_LAST_CHANGE_TIME_COL = 9; + static final int V2V4_COMPOSITE_PACK_COL = 10; // renamed from Internal Alignment + static final int V2V4_COMPOSITE_MIN_ALIGN_COL = 11; // renamed from External Alignment - static final Schema V2_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", + static final Schema V2V4_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, @@ -84,6 +84,16 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla } } + @Override + int getVersion() { + return compositeTable.getSchema().getVersion(); + } + + @Override + int getRecordCount() { + return compositeTable.getRecordCount(); + } + @Override public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, @@ -127,7 +137,7 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla @Override Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return compositeTable.findRecords(new LongField(archiveID), - V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL); + V2V4_COMPOSITE_SOURCE_ARCHIVE_ID_COL); } /* (non-Javadoc) @@ -139,36 +149,36 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla return null; } DBRecord rec = CompositeDBAdapter.COMPOSITE_SCHEMA.createRecord(oldRec.getKey()); - rec.setString(COMPOSITE_NAME_COL, oldRec.getString(V2_COMPOSITE_NAME_COL)); - rec.setString(COMPOSITE_COMMENT_COL, oldRec.getString(V2_COMPOSITE_COMMENT_COL)); + rec.setString(COMPOSITE_NAME_COL, oldRec.getString(V2V4_COMPOSITE_NAME_COL)); + rec.setString(COMPOSITE_COMMENT_COL, oldRec.getString(V2V4_COMPOSITE_COMMENT_COL)); rec.setBooleanValue(COMPOSITE_IS_UNION_COL, - oldRec.getBooleanValue(V2_COMPOSITE_IS_UNION_COL)); - rec.setLongValue(COMPOSITE_CAT_COL, oldRec.getLongValue(V2_COMPOSITE_CAT_COL)); - rec.setIntValue(COMPOSITE_LENGTH_COL, oldRec.getIntValue(V2_COMPOSITE_LENGTH_COL)); + oldRec.getBooleanValue(V2V4_COMPOSITE_IS_UNION_COL)); + rec.setLongValue(COMPOSITE_CAT_COL, oldRec.getLongValue(V2V4_COMPOSITE_CAT_COL)); + rec.setIntValue(COMPOSITE_LENGTH_COL, oldRec.getIntValue(V2V4_COMPOSITE_LENGTH_COL)); rec.setIntValue(COMPOSITE_ALIGNMENT_COL, -1); rec.setIntValue(COMPOSITE_NUM_COMPONENTS_COL, - oldRec.getIntValue(V2_COMPOSITE_NUM_COMPONENTS_COL)); + oldRec.getIntValue(V2V4_COMPOSITE_NUM_COMPONENTS_COL)); rec.setLongValue(COMPOSITE_SOURCE_ARCHIVE_ID_COL, - oldRec.getLongValue(V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL)); + oldRec.getLongValue(V2V4_COMPOSITE_SOURCE_ARCHIVE_ID_COL)); rec.setLongValue(COMPOSITE_UNIVERSAL_DT_ID, - oldRec.getLongValue(V2_COMPOSITE_UNIVERSAL_DT_ID_COL)); + oldRec.getLongValue(V2V4_COMPOSITE_UNIVERSAL_DT_ID_COL)); rec.setLongValue(COMPOSITE_SOURCE_SYNC_TIME_COL, - oldRec.getLongValue(V2_COMPOSITE_SOURCE_SYNC_TIME_COL)); + oldRec.getLongValue(V2V4_COMPOSITE_SOURCE_SYNC_TIME_COL)); rec.setLongValue(COMPOSITE_LAST_CHANGE_TIME_COL, - oldRec.getLongValue(V2_COMPOSITE_LAST_CHANGE_TIME_COL)); - rec.setIntValue(COMPOSITE_PACKING_COL, oldRec.getIntValue(V2_COMPOSITE_PACK_COL)); - rec.setIntValue(COMPOSITE_MIN_ALIGN_COL, oldRec.getIntValue(V2_COMPOSITE_MIN_ALIGN_COL)); + oldRec.getLongValue(V2V4_COMPOSITE_LAST_CHANGE_TIME_COL)); + rec.setIntValue(COMPOSITE_PACKING_COL, oldRec.getIntValue(V2V4_COMPOSITE_PACK_COL)); + rec.setIntValue(COMPOSITE_MIN_ALIGN_COL, oldRec.getIntValue(V2V4_COMPOSITE_MIN_ALIGN_COL)); return rec; } @Override DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()), - V2_COMPOSITE_UNIVERSAL_DT_ID_COL); + V2V4_COMPOSITE_UNIVERSAL_DT_ID_COL); for (Field key : keys) { DBRecord record = compositeTable.getRecord(key); - if (record.getLongValue(V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { + if (record.getLongValue(V2V4_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { return translateRecord(record); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java similarity index 58% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java index 5d7ba54b78..b652059788 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java @@ -18,35 +18,41 @@ package ghidra.program.database.data; import java.io.IOException; import java.util.Date; +import javax.help.UnsupportedOperationException; + import db.*; import ghidra.program.model.data.CompositeInternal; import ghidra.util.UniversalID; import ghidra.util.exception.VersionException; /** - * Version 5 implementation for accessing the Composite database table. - * This version introduces the retained computed alignment to reduce + * Version 5 and 6 implementation for accessing the Composite database table. + * Version 5 introduced the retained computed alignment to reduce the * need for recalculation and to allow for improved change detection. + * Version 6 did not change the schema but corresponds to the elimination + * of Structure flex-arrays which are supported in read-only mode under + * the older version 5 adapter version. */ -class CompositeDBAdapterV5 extends CompositeDBAdapter { +class CompositeDBAdapterV5V6 extends CompositeDBAdapter { - static final int VERSION = 5; + static final int V5_VERSION = 5; + static final int VERSION = 6; - static final int V5_COMPOSITE_NAME_COL = 0; - static final int V5_COMPOSITE_COMMENT_COL = 1; - static final int V5_COMPOSITE_IS_UNION_COL = 2; - static final int V5_COMPOSITE_CAT_COL = 3; - static final int V5_COMPOSITE_LENGTH_COL = 4; - static final int V5_COMPOSITE_ALIGNMENT_COL = 5; - static final int V5_COMPOSITE_NUM_COMPONENTS_COL = 6; - static final int V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL = 7; - static final int V5_COMPOSITE_UNIVERSAL_DT_ID_COL = 8; - static final int V5_COMPOSITE_SOURCE_SYNC_TIME_COL = 9; - static final int V5_COMPOSITE_LAST_CHANGE_TIME_COL = 10; - static final int V5_COMPOSITE_PACK_COL = 11; - static final int V5_COMPOSITE_MIN_ALIGN_COL = 12; + static final int V5V6_COMPOSITE_NAME_COL = 0; + static final int V5V6_COMPOSITE_COMMENT_COL = 1; + static final int V5V6_COMPOSITE_IS_UNION_COL = 2; + static final int V5V6_COMPOSITE_CAT_COL = 3; + static final int V5V6_COMPOSITE_LENGTH_COL = 4; + static final int V5V6_COMPOSITE_ALIGNMENT_COL = 5; + static final int V5V6_COMPOSITE_NUM_COMPONENTS_COL = 6; + static final int V5V6_COMPOSITE_SOURCE_ARCHIVE_ID_COL = 7; + static final int V5V6_COMPOSITE_UNIVERSAL_DT_ID_COL = 8; + static final int V5V6_COMPOSITE_SOURCE_SYNC_TIME_COL = 9; + static final int V5V6_COMPOSITE_LAST_CHANGE_TIME_COL = 10; + static final int V5V6_COMPOSITE_PACK_COL = 11; + static final int V5V6_COMPOSITE_MIN_ALIGN_COL = 12; - static final Schema V5_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", + static final Schema V5V6_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, @@ -60,16 +66,16 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { /** * Gets an adapter for the Composite database table. * @param handle handle to the database containing the table. - * @param create true if this constructor should create the table. + * @param openMode * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if IO error occurs */ - public CompositeDBAdapterV5(DBHandle handle, boolean create) + public CompositeDBAdapterV5V6(DBHandle handle, int openMode) throws VersionException, IOException { - if (create) { - compositeTable = handle.createTable(COMPOSITE_TABLE_NAME, V5_COMPOSITE_SCHEMA, - new int[] { V5_COMPOSITE_CAT_COL, V5_COMPOSITE_UNIVERSAL_DT_ID_COL }); + if (openMode == DBConstants.CREATE) { + compositeTable = handle.createTable(COMPOSITE_TABLE_NAME, V5V6_COMPOSITE_SCHEMA, + new int[] { V5V6_COMPOSITE_CAT_COL, V5V6_COMPOSITE_UNIVERSAL_DT_ID_COL }); } else { compositeTable = handle.getTable(COMPOSITE_TABLE_NAME); @@ -77,6 +83,9 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME); } int version = compositeTable.getSchema().getVersion(); + if (version == V5_VERSION && openMode == DBConstants.READ_ONLY) { + return; // StructureDB handles read-only flex-array migration + } if (version != VERSION) { String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + " but got " + version; @@ -88,10 +97,22 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { } } + int getVersion() { + return compositeTable.getSchema().getVersion(); + } + + @Override + int getRecordCount() { + return compositeTable.getRecordCount(); + } + @Override public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime, int packValue, int minAlignment) throws IOException { + if (compositeTable.getSchema().getVersion() == V5_VERSION) { + throw new UnsupportedOperationException(); + } if (packValue < CompositeInternal.DEFAULT_ALIGNMENT) { packValue = CompositeInternal.NO_PACKING; } @@ -102,19 +123,19 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { DataTypeManagerDB.createKey(DataTypeManagerDB.COMPOSITE, compositeTable.getKey()); DBRecord record = CompositeDBAdapter.COMPOSITE_SCHEMA.createRecord(key); - record.setString(V5_COMPOSITE_NAME_COL, name); - record.setString(V5_COMPOSITE_COMMENT_COL, comments); - record.setBooleanValue(V5_COMPOSITE_IS_UNION_COL, isUnion); - record.setLongValue(V5_COMPOSITE_CAT_COL, categoryID); - record.setIntValue(V5_COMPOSITE_LENGTH_COL, length); - record.setIntValue(V5_COMPOSITE_ALIGNMENT_COL, computedAlignment); - record.setIntValue(V5_COMPOSITE_NUM_COMPONENTS_COL, length); - record.setLongValue(V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL, sourceArchiveID); - record.setLongValue(V5_COMPOSITE_UNIVERSAL_DT_ID_COL, sourceDataTypeID); - record.setLongValue(V5_COMPOSITE_SOURCE_SYNC_TIME_COL, lastChangeTime); - record.setLongValue(V5_COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime); - record.setIntValue(V5_COMPOSITE_PACK_COL, packValue); - record.setIntValue(V5_COMPOSITE_MIN_ALIGN_COL, minAlignment); + record.setString(V5V6_COMPOSITE_NAME_COL, name); + record.setString(V5V6_COMPOSITE_COMMENT_COL, comments); + record.setBooleanValue(V5V6_COMPOSITE_IS_UNION_COL, isUnion); + record.setLongValue(V5V6_COMPOSITE_CAT_COL, categoryID); + record.setIntValue(V5V6_COMPOSITE_LENGTH_COL, length); + record.setIntValue(V5V6_COMPOSITE_ALIGNMENT_COL, computedAlignment); + record.setIntValue(V5V6_COMPOSITE_NUM_COMPONENTS_COL, length); + record.setLongValue(V5V6_COMPOSITE_SOURCE_ARCHIVE_ID_COL, sourceArchiveID); + record.setLongValue(V5V6_COMPOSITE_UNIVERSAL_DT_ID_COL, sourceDataTypeID); + record.setLongValue(V5V6_COMPOSITE_SOURCE_SYNC_TIME_COL, lastChangeTime); + record.setLongValue(V5V6_COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime); + record.setIntValue(V5V6_COMPOSITE_PACK_COL, packValue); + record.setIntValue(V5V6_COMPOSITE_MIN_ALIGN_COL, minAlignment); compositeTable.putRecord(record); return record; } @@ -131,6 +152,9 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { @Override public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + if (compositeTable.getSchema().getVersion() == V5_VERSION) { + throw new UnsupportedOperationException(); + } if (setLastChangeTime) { record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, (new Date()).getTime()); @@ -140,6 +164,9 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { @Override public boolean removeRecord(long compositeID) throws IOException { + if (compositeTable.getSchema().getVersion() == V5_VERSION) { + throw new UnsupportedOperationException(); + } return compositeTable.deleteRecord(compositeID); } @@ -157,17 +184,17 @@ class CompositeDBAdapterV5 extends CompositeDBAdapter { @Override Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return compositeTable.findRecords(new LongField(archiveID), - V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL); + V5V6_COMPOSITE_SOURCE_ARCHIVE_ID_COL); } @Override DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()), - V5_COMPOSITE_UNIVERSAL_DT_ID_COL); + V5V6_COMPOSITE_UNIVERSAL_DT_ID_COL); for (Field key : keys) { DBRecord record = compositeTable.getRecord(key); - if (record.getLongValue(V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { + if (record.getLongValue(V5V6_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { return record; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java index 02b679a601..62147d9e85 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import java.io.IOException; @@ -35,11 +32,10 @@ class DataTypeComponentDB implements InternalDataTypeComponent { private final DataTypeManagerDB dataMgr; private final ComponentDBAdapter adapter; - private final DBRecord record; // null record -> undefined component - private final Composite parent; + private final DBRecord record; // null record -> immutable component + private final CompositeDB parent; private DataType cachedDataType; // required for bit-fields during packing process - private boolean isFlexibleArrayComponent = false; private int ordinal; private int offset; @@ -47,37 +43,48 @@ class DataTypeComponentDB implements InternalDataTypeComponent { private SettingsDBManager defaultSettings; /** - * Construct undefined component not backed by a record. - * Changes to component will be ignored. + * Construct an immutable component not backed by a record with a specified datatype and length. + * No comment or field name is provided. * @param dataMgr - * @param cache - * @param adapter - * @param parentID + * @param parent + * @param ordinal + * @param offset + * @param datatype + * @param length + */ + DataTypeComponentDB(DataTypeManagerDB dataMgr, CompositeDB parent, int ordinal, int offset, + DataType datatype, int length) { + this(dataMgr, parent, ordinal, offset); + this.cachedDataType = datatype; + this.length = length; + } + + /** + * Construct an immutable undefined 1-byte component not backed by a record. + * No comment or field name is provided. + * @param dataMgr + * @param parent * @param ordinal * @param offset */ - DataTypeComponentDB(DataTypeManagerDB dataMgr, ComponentDBAdapter adapter, Composite parent, - long parentID, int ordinal, int offset) { + DataTypeComponentDB(DataTypeManagerDB dataMgr, CompositeDB parent, int ordinal, int offset) { this.dataMgr = dataMgr; - this.adapter = adapter; this.parent = parent; this.ordinal = ordinal; this.offset = offset; this.length = 1; this.record = null; + this.adapter = null; } /** * Construct a component backed by a record. - *

- * NOTE: A structure flexible array component is identified by length=0, ordinal=-1 and - * offset=-1). * @param dataMgr - * @param cache * @param adapter + * @param parent * @param record */ - DataTypeComponentDB(DataTypeManagerDB dataMgr, ComponentDBAdapter adapter, Composite parent, + DataTypeComponentDB(DataTypeManagerDB dataMgr, ComponentDBAdapter adapter, CompositeDB parent, DBRecord record) { this.dataMgr = dataMgr; this.adapter = adapter; @@ -86,17 +93,9 @@ class DataTypeComponentDB implements InternalDataTypeComponent { ordinal = record.getIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL); offset = record.getIntValue(ComponentDBAdapter.COMPONENT_OFFSET_COL); length = record.getIntValue(ComponentDBAdapter.COMPONENT_SIZE_COL); - initFlexibleArrayComponent(); - } - - private void initFlexibleArrayComponent() { - isFlexibleArrayComponent = - length == 0 && offset < 0 && ordinal < 0 && (parent instanceof Structure); - } - - @Override - public boolean isFlexibleArrayComponent() { - return isFlexibleArrayComponent; + if (isZeroBitFieldComponent()) { + length = 0; // previously stored as 1, force to 0 + } } @Override @@ -127,12 +126,12 @@ class DataTypeComponentDB implements InternalDataTypeComponent { @Override public DataType getDataType() { - if (record == null) { - return DataType.DEFAULT; - } if (cachedDataType != null) { return cachedDataType; } + if (record == null) { + return DataType.DEFAULT; + } long id = record.getLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL); if (id == -1) { return DataType.DEFAULT; @@ -147,35 +146,25 @@ class DataTypeComponentDB implements InternalDataTypeComponent { @Override public int getOffset() { - if (isFlexibleArrayComponent) { - if (parent.isZeroLength()) { - // some structures have only a flexible array defined - return 0; - } - return parent.getLength(); - } return offset; } boolean containsOffset(int off) { - if (isFlexibleArrayComponent) { - return false; + if (off == offset) { // separate check required to handle zero-length case + return true; } - return off >= offset && off <= (offset + length - 1); + return off > offset && off < (offset + length); } @Override public int getOrdinal() { - if (isFlexibleArrayComponent) { - return parent.getNumComponents(); - } return ordinal; } @Override public int getEndOffset() { - if (isFlexibleArrayComponent) { - return -1; + if (length == 0) { // separate check required to handle zero-length case + return offset; } return offset + length - 1; } @@ -242,17 +231,6 @@ class DataTypeComponentDB implements InternalDataTypeComponent { return null; } - @Override - public String getDefaultFieldName() { - if (isZeroBitFieldComponent()) { - return ""; - } - if (parent instanceof Structure) { - return DEFAULT_FIELD_NAME_PREFIX + "_0x" + Integer.toHexString(getOffset()); - } - return DEFAULT_FIELD_NAME_PREFIX + ordinal; - } - @Override public void setFieldName(String name) throws DuplicateNameException { try { @@ -304,10 +282,8 @@ class DataTypeComponentDB implements InternalDataTypeComponent { DataType myDt = getDataType(); DataType otherDt = dtc.getDataType(); - // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from - // stored values for flexible array component - if (getOffset() != dtc.getOffset() || getLength() != dtc.getLength() || - getOrdinal() != dtc.getOrdinal() || + if (offset != dtc.getOffset() || getLength() != dtc.getLength() || + ordinal != dtc.getOrdinal() || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; @@ -346,9 +322,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent { boolean aligned = (myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false; // Components don't need to have matching offset when they are aligned - // NOTE: use getOffset() method since returned values will differ from - // stored values for flexible array component - if ((!aligned && (getOffset() != dtc.getOffset())) || + if ((!aligned && (offset != dtc.getOffset())) || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; @@ -364,9 +338,6 @@ class DataTypeComponentDB implements InternalDataTypeComponent { @Override public void update(int newOrdinal, int newOffset, int newLength) { - if (isFlexibleArrayComponent) { - return; - } if (length < 0) { throw new IllegalArgumentException( "Cannot set data type component length to " + length + "."); @@ -385,9 +356,6 @@ class DataTypeComponentDB implements InternalDataTypeComponent { } void setOffset(int newOffset, boolean updateRecord) { - if (isFlexibleArrayComponent) { - return; - } offset = newOffset; if (record != null) { record.setIntValue(ComponentDBAdapter.COMPONENT_OFFSET_COL, offset); @@ -398,9 +366,6 @@ class DataTypeComponentDB implements InternalDataTypeComponent { } void setOrdinal(int ordinal, boolean updateRecord) { - if (isFlexibleArrayComponent) { - return; - } this.ordinal = ordinal; if (record != null) { record.setIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL, ordinal); @@ -411,9 +376,6 @@ class DataTypeComponentDB implements InternalDataTypeComponent { } void setLength(int length, boolean updateRecord) { - if (isFlexibleArrayComponent || length == this.length) { - return; - } this.length = length; if (length < 0) { throw new IllegalArgumentException( @@ -454,21 +416,14 @@ class DataTypeComponentDB implements InternalDataTypeComponent { @Override public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(" " + getOrdinal()); - buffer.append(" " + getOffset()); - DataType dt = getDataType(); - buffer.append(" " + dt.getName()); - if (isFlexibleArrayComponent) { - buffer.append("[0]"); - } - else if (dt instanceof BitFieldDataType) { - buffer.append("(" + ((BitFieldDataType) dt).getBitOffset() + ")"); - } - buffer.append(" " + getLength()); - buffer.append(" " + getFieldName()); - String comment = getComment(); - buffer.append(" " + ((comment != null) ? ("\"" + comment + "\"") : comment)); - return buffer.toString(); + return InternalDataTypeComponent.toString(this); + } + + /** + * Determine if component is an undefined filler component + * @return true if undefined filler component, else false + */ + boolean isUndefined() { + return record == null && cachedDataType == null; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index 655cb99327..bf151a9912 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -165,11 +165,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } /** - * Construct a temporary data-type manager. Note that this manager does not + * Construct a new temporary data-type manager. Note that this manager does not * support the save or saveAs operation. + * @param dataOrganization applicable data organization */ - protected DataTypeManagerDB() { + protected DataTypeManagerDB(DataOrganization dataOrganization) { this.lock = new Lock("DataTypeManagerDB"); + this.dataOrganization = dataOrganization; try { dbHandle = new DBHandle(); @@ -193,6 +195,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { /** * Constructor for a data-type manager backed by a packed database file. When * opening for UPDATE an automatic upgrade will be performed if required. + * NOTE: default DataOrganization will be used. * * @param packedDBfile packed datatype archive file (i.e., *.gdt resource). * @param openMode open mode CREATE, READ_ONLY or UPDATE (see @@ -241,6 +244,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager { // Initialize datatype manager and save new archive on CREATE boolean initSuccess = false; try { + + // TODO: In the future it may be neccessary to store additional properties + // within the archive to failitate use of a language specified data organization. + // It will likely be neccessary to have a different CREATE constructor which accepts + // a DataOrganization + + dataOrganization = DataOrganizationImpl.getDefaultOrganization(); + initPackedDatabase(packedDBfile, openMode); if (openMode == DBConstants.CREATE) { @@ -271,15 +282,18 @@ abstract public class DataTypeManagerDB implements DataTypeManager { catch (VersionException e) { if (openMode == DBConstants.UPDATE && e.isUpgradable()) { try { - Msg.info(this, - "Performing datatype archive schema upgrade: " + packedDBfile.getName()); init(DBConstants.UPGRADE, TaskMonitor.DUMMY); + migrateOldFlexArrayComponentsIfRequired(TaskMonitor.DUMMY); + + Msg.showInfo(this, null, "Archive Upgraded", + "Data type archive schema has been upgraded: " + packedDBfile.getName()); } catch (VersionException ve) { throw new IOException(e); // unexpected } } else { + // TODO: Unable to handle required upgrade for read-only without API change throw new IOException(e); } } @@ -317,7 +331,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { updateID(); initializeAdapters(openMode, monitor); if (checkForSourceArchiveUpdatesNeeded(openMode, monitor)) { - doSourceArchiveUpdates(null, TaskMonitor.DUMMY); + doSourceArchiveUpdates(null, monitor); } dtCache = new DBObjectCache<>(10); sourceArchiveDBCache = new DBObjectCache<>(10); @@ -469,12 +483,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager { for (DataTypeComponent comp : comps) { comp.getDataType().addParent(dt); } - if (dt instanceof Structure) { - Structure struct = (Structure) dt; - if (struct.hasFlexibleArrayComponent()) { - struct.getFlexibleArrayComponent().getDataType().addParent(dt); - } - } } else if (dt instanceof FunctionDefinition) { FunctionDefinition funDef = (FunctionDefinition) dt; @@ -528,7 +536,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return; } String name = dataTypePath.getDataTypeName(); - DataType compareDataType = new TypedefDataType(name, DefaultDataType.dataType); + DataType compareDataType = new TypedefDataType(name, DataType.DEFAULT); try { compareDataType.setCategoryPath(dataTypePath.getCategoryPath()); } @@ -1246,9 +1254,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager { // Dynamic or FactoryDataType lock.acquire(); try { + // Don't support replacement with Factory or Dynamic datatype + if (replacementDt instanceof Dynamic || replacementDt instanceof FactoryDataType) { + throw new IllegalArgumentException( + "Datatype replacment with dynamic or factory type not permitted."); + } if (getID(existingDt) < 0) { throw new IllegalArgumentException( - "datatype to replace is not contained in this datatype manager."); + "Datatype to replace is not contained in this datatype manager."); } boolean fixupName = false; if (!contains(replacementDt)) { @@ -1308,6 +1321,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (existingDt == replacementDt) { return; } + DataTypePath replacedDtPath = existingDt.getDataTypePath(); long replacedId = getID(existingDt); @@ -1411,14 +1425,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (name == null || name.length() == 0) { return; } - if (name.equals(DefaultDataType.dataType.getName())) { - list.add(DefaultDataType.dataType); + if (name.equals(DataType.DEFAULT.getName())) { + list.add(DataType.DEFAULT); return; } lock.acquire(); try { buildSortedDataTypeList(); - DataType compareDataType = new TypedefDataType(name, DefaultDataType.dataType); + DataType compareDataType = new TypedefDataType(name, DataType.DEFAULT); int index = Collections.binarySearch(sortedDataTypes, compareDataType, nameComparator); if (index < 0) { index = -index - 1; @@ -1443,8 +1457,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (name == null || name.length() == 0) { return; } - if (name.equals(DefaultDataType.dataType.getName())) { - list.add(DefaultDataType.dataType); + if (name.equals(DataType.DEFAULT.getName())) { + list.add(DataType.DEFAULT); return; } if (monitor == null) { @@ -2121,6 +2135,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { dt.setDefaultSettings(new SettingsDBManager(this, dt, dataTypeID)); } catch (Exception e) { + Msg.error(this, e); dt = new MissingBuiltInDataType(catPath, name, classPath, this); } builtInMap.put(key, dt); @@ -2515,27 +2530,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private Array createArray(DataType dt, int numElements, int elementLength, Category cat, DataTypeConflictHandler handler) throws IOException { - if (dt instanceof FactoryDataType) { - throw new IllegalArgumentException( - "Array data-type may not be a Factory data-type: " + dt.getName()); - } - if (dt instanceof Dynamic && !((Dynamic) dt).canSpecifyLength()) { - throw new IllegalArgumentException( - "Array data-type may not be a non-sizable Dynamic data-type: " + dt.getName()); - } - if (elementLength <= 0) { - throw new IllegalArgumentException("Array data-type must be Fixed length"); - } - if (numElements <= 0) { - throw new IllegalArgumentException( - "number of array elements must be positive, not " + numElements); - } dt = resolve(dt, handler); long dataTypeID = getResolvedID(dt); if (!(dt instanceof Dynamic)) { elementLength = -1; } + // defer to ArrayDataType for checks + new ArrayDataType(dt, numElements, elementLength, this); + DBRecord record = arrayAdapter.createRecord(dataTypeID, numElements, elementLength, cat.getID()); addParentChildRecord(record.getKey(), dataTypeID); @@ -3777,12 +3780,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } private boolean checkForSourceArchiveUpdatesNeeded(int openMode, TaskMonitor monitor) - throws IOException { + throws IOException, CancelledException { if (openMode == DBConstants.CREATE || openMode == DBConstants.READ_ONLY) { return false; } List records = sourceArchiveAdapter.getRecords(); for (DBRecord record : records) { + monitor.checkCanceled(); if (SourceArchiveUpgradeMap.isReplacedSourceArchive(record.getKey())) { return true; } @@ -3790,12 +3794,41 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return false; } + /** + * During any UPGRADE instantiation this method should be invoked with an open transaction + * once the associated DomainObject is ready. This late stage upgrade is required since + * it may entail resolving new array datatypes which requires this manager to be in a + * fully functional state. + * @param monitor task monitor + * @throws IOException if an IO error occurs on database + * @throws CancelledException if monitor is cancelled + */ + protected void migrateOldFlexArrayComponentsIfRequired(TaskMonitor monitor) + throws IOException, CancelledException { + + if (!compositeAdapter.isFlexArrayMigrationRequired()) { + return; + } + + RecordIterator records = compositeAdapter.getRecords(); + while (records.hasNext()) { + monitor.checkCanceled(); + DBRecord rec = records.next(); + if (!rec.getBooleanValue(CompositeDBAdapter.COMPOSITE_IS_UNION_COL)) { + // StructureDB instantiation will perform an automatic flex-array + // record migration if needed. + new StructureDB(this, dtCache, compositeAdapter, componentAdapter, rec); + } + } + + } + /** * This method is only invoked during an upgrade. * * @param compilerSpec compiler spec * @param monitor task monitor - * @throws CancelledException if task cacn + * @throws CancelledException if task cancelled */ protected void doSourceArchiveUpdates(CompilerSpec compilerSpec, TaskMonitor monitor) throws CancelledException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeProxyComponentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeProxyComponentDB.java new file mode 100644 index 0000000000..c4c8e6fc26 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeProxyComponentDB.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import ghidra.program.model.data.DataType; + +/** + * DataTypeProxyComponentDB facilitates a datatype/component substitution when a + * DataTypeManagerDB is constructed for read-only use and datatype migration is required. + * An example of this is the {@link StructureDB} migration of flex-arrays to a zero-element array. + */ +class DataTypeProxyComponentDB extends DataTypeComponentDB { + + private final String fieldName; + private final String comment; + + /** + * Construct a DataTypeComponentDB with specific component characteristics and without a record. + * @param dataMgr associated datatype manager + * @param parent composite datatype which contains this component + * @param ordinal component ordinal + * @param offset component offset + * @param length component length + * @param datatype component datatype + * @param fieldName component field name or null + * @param comment component comment or null + */ + DataTypeProxyComponentDB(DataTypeManagerDB dataMgr, CompositeDB parent, int ordinal, int offset, + DataType datatype, int length, String fieldName, String comment) { + super(dataMgr, parent, ordinal, offset, datatype, length); + this.fieldName = fieldName; + this.comment = comment; + } + + @Override + public String getFieldName() { + return fieldName; + } + + @Override + public String getComment() { + return comment; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java index 833ab4d332..3d72ba76c7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeUtilities.java @@ -174,12 +174,6 @@ public class DataTypeUtilities { return true; } } - if (firstDataType instanceof Structure) { - DataTypeComponent flexDtc = ((Structure) firstDataType).getFlexibleArrayComponent(); - if (flexDtc != null && isSecondPartOfFirst(flexDtc.getDataType(), secondDataType)) { - return true; - } - } } return false; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java index 2c9a15fced..d3f034dd9f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java index a9c41bdb60..6227e01521 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java @@ -72,14 +72,15 @@ public class ProgramDataTypeManager extends DataTypeManagerDB this.program = p; dataOrganization = p.getCompilerSpec().getDataOrganization(); removeOldFileNameList(); + if (upgrade) { + removeOldFileNameList(); + } } private void removeOldFileNameList() { - if (upgrade) { - Options options = program.getOptions(Program.PROGRAM_INFO); - if (options.contains(OLD_DT_ARCHIVE_FILENAMES)) { - options.removeOption(OLD_DT_ARCHIVE_FILENAMES); - } + Options options = program.getOptions(Program.PROGRAM_INFO); + if (options.contains(OLD_DT_ARCHIVE_FILENAMES)) { + options.removeOption(OLD_DT_ARCHIVE_FILENAMES); } } @@ -93,6 +94,7 @@ public class ProgramDataTypeManager extends DataTypeManagerDB throws IOException, CancelledException { if (openMode == DBConstants.UPGRADE) { doSourceArchiveUpdates(program.getCompilerSpec(), monitor); + migrateOldFlexArrayComponentsIfRequired(monitor); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java index a7ad073dd5..dbdc61f819 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java @@ -34,18 +34,13 @@ import ghidra.util.task.TaskMonitor; /** * Class for managing data types in a project archive + * NOTE: default data organization is used. */ public class ProjectDataTypeManager extends DataTypeManagerDB implements ProjectArchiveBasedDataTypeManager { -// private static final String DT_ARCHIVE_FILENAMES = "DataTypeArchiveFilenames"; -// private static final String FILENAME_SEPARATOR = ";"; -// private static final String ARCHIVE_DIR = "typeinfo"; -// private static final String RELATIVE_PATH_PREFIX = "."; private DataTypeArchiveDB dataTypeArchive; -// private ArrayList filenameList = new ArrayList(); // Archives used to get data types for this program. - /** * Constructor * @param handle open database handle @@ -63,15 +58,13 @@ public class ProjectDataTypeManager extends DataTypeManagerDB } /** - * @see ghidra.program.database.ManagerDB#setProgram(ghidra.program.database.ProgramDB) + * Set the associated Archive + * @param dtArchive associated archive */ public void setDataTypeArchive(DataTypeArchiveDB dtArchive) { this.dataTypeArchive = dtArchive; } - /** - * @see ghidra.program.model.data.DataTypeManager#getName() - */ @Override public String getName() { return dataTypeArchive.getDomainFile().getName(); @@ -82,9 +75,6 @@ public class ProjectDataTypeManager extends DataTypeManagerDB return PointerDataType.getPointer(dt, dataTypeArchive.getDefaultPointerSize()); } - /** - * @see ghidra.program.model.data.DataTypeManager#setName(java.lang.String) - */ @Override public void setName(String name) throws InvalidNameException { if (name == null || name.length() == 0) { @@ -234,9 +224,11 @@ public class ProjectDataTypeManager extends DataTypeManagerDB return ArchiveType.PROJECT; } - public void archiveReady(int openMode, TaskMonitor monitor) throws CancelledException { + public void archiveReady(int openMode, TaskMonitor monitor) + throws IOException, CancelledException { if (openMode == DBConstants.UPGRADE) { doSourceArchiveUpdates(null, monitor); + migrateOldFlexArrayComponentsIfRequired(monitor); } } 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 0fd9664240..30a698c42d 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 @@ -41,7 +41,6 @@ class StructureDB extends CompositeDB implements StructureInternal { private int numComponents; // If packed, this does not include the undefined components. private List components; - private DataTypeComponentDB flexibleArrayComponent; /** * Constructor @@ -64,29 +63,110 @@ class StructureDB extends CompositeDB implements StructureInternal { components = new ArrayList<>(); try { + DBRecord oldFlexArrayRecord = null; Field[] ids = componentAdapter.getComponentIdsInComposite(key); for (Field id : ids) { DBRecord rec = componentAdapter.getRecord(id.getLongValue()); + if (rec.getIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL) < 0) { + // old flex-array component record - defer migration (see below) + oldFlexArrayRecord = rec; + continue; + } DataTypeComponentDB component = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); - if (component.isFlexibleArrayComponent()) { - flexibleArrayComponent = component; - } - else { - components.add(component); - } + components.add(component); + } + + Collections.sort(components, ComponentComparator.INSTANCE); + + structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL); + structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL); + computedAlignment = -1; + numComponents = record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL); + + if (oldFlexArrayRecord != null) { + migrateOldFlexArray(oldFlexArrayRecord); } } catch (IOException e) { dataMgr.dbError(e); } + } - Collections.sort(components, ComponentComparator.INSTANCE); + /** + * Eliminate use of old trailing flex-array specification which is now specified using + * a zero-element array. Due to the specification of a new array datatype this must handle + * two cases when an old flex-array component is exists: + *

    + *
  1. read-only case: associated {@link DataTypeManagerDB} is not updateable. This is the normal + * case for an open archive. A non-DB ArrayDataType must be employed with an immutable + * {@link DataTypeProxyComponentDB} in place of the flex-array component.
  2. + *
  3. upgrade (open for update with open transaction): the flex-array record is modified to + * to indicate an appropriate resolved zero-element array. + *
+ *

+ * NOTE: When {@link DataTypeManagerDB} is instantiated for update an upgrade must be forced based upon the + * {@link CompositeDBAdapter#isFlexArrayMigrationRequired()} indicator. The upgrade logic + * (see {@link DataTypeManagerDB#migrateOldFlexArrayComponentsIfRequired(ghidra.util.task.TaskMonitor)}) + * istantiates all structures within the databases with an open transaaction allowing this method + * to perform the neccessary flex-array record migration. + *

+ * NOTE: The offset of the migrated flex array component and structure length may change during upgrade + * when packing is enabled when the original packed structure length did not properly factor the flex-array + * alignment. Repack does not occur in the read-only case. + * + * @param oldFlexArrayRecord record which corresponds to an olf flex-array component + * @throws IOException if a database error occurs + */ + private void migrateOldFlexArray(DBRecord oldFlexArrayRecord) throws IOException { + long id = oldFlexArrayRecord.getLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL); + DataType dt = dataMgr.getDataType(id); // could be BadDataType if built-in type is missing - structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL); - structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL); - computedAlignment = -1; - numComponents = record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL); + // use zero-element array (need positive element length if dt is BadDataType) + dt = new ArrayDataType(dt, 0, 1, dataMgr); + + boolean repack = false; + + DataTypeComponentDB component; + if (dataMgr.isUpdatable()) { + if (!dataMgr.isTransactionActive()) { + throw new AssertException( + "Structure flex-array component should have been migrated during required upgrade: " + + getPathName()); + } + dt = dataMgr.resolve(dt, null); // use zero-length array + oldFlexArrayRecord.setIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL, + numComponents++); + oldFlexArrayRecord.setIntValue(ComponentDBAdapter.COMPONENT_OFFSET_COL, + structLength); + oldFlexArrayRecord.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL, + dataMgr.getID(dt)); + componentAdapter.updateRecord(oldFlexArrayRecord); + + component = new DataTypeComponentDB(dataMgr, componentAdapter, this, + oldFlexArrayRecord); + + // Update component count (flex-array had been excluded prviously) + record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, + numComponents); + compositeAdapter.updateRecord(record, false); + repack = isPackingEnabled(); + } + else { + // read-only mode must use proxy component with zero-length array + String fieldName = + oldFlexArrayRecord.getString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL); + String comment = + oldFlexArrayRecord.getString(ComponentDBAdapter.COMPONENT_COMMENT_COL); + component = new DataTypeProxyComponentDB(dataMgr, this, numComponents++, + structLength, dt, 0, fieldName, comment); + } + + components.add(component); + + if (repack) { + repack(false, false); // repack during upgrade only when packing enabled + } } @Override @@ -112,8 +192,6 @@ class StructureDB extends CompositeDB implements StructureInternal { boolean validatePackAndNotify) throws DataTypeDependencyException, IllegalArgumentException { - // TODO: May want to standardize flex-array use with StructureDataType - lock.acquire(); try { checkDeleted(); @@ -127,9 +205,8 @@ class StructureDB extends CompositeDB implements StructureInternal { DataTypeComponentDB dtc = null; try { if (dataType == DataType.DEFAULT) { - // FIXME: verify - does not appear to modify structure - dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, key, - numComponents, structLength); + // Structre will grow by 1-byte below (ignored by packed structure) + dtc = new DataTypeComponentDB(dataMgr, this, numComponents, structLength); } else { int componentLength = getPreferredComponentLength(dataType, length); @@ -141,7 +218,7 @@ class StructureDB extends CompositeDB implements StructureInternal { } int structureGrowth = dtc.getLength(); - if (!isPackingEnabled() && length > 0) { + if (structureGrowth != 0 && !isPackingEnabled() && length > 0) { structureGrowth = length; } @@ -167,59 +244,6 @@ class StructureDB extends CompositeDB implements StructureInternal { } } - private DataTypeComponent doAddFlexArray(DataType dataType, String name, String comment, - boolean validatePackAndNotify) - throws DataTypeDependencyException, IllegalArgumentException { - - // TODO: May want to standardize implementation with StructureDataType - - lock.acquire(); - try { - checkDeleted(); - - if (validatePackAndNotify) { - - if (isInvalidFlexArrayDataType(dataType)) { - throw new IllegalArgumentException( - "Unsupported flexType: " + dataType.getDisplayName()); - } - - validateDataType(dataType); - dataType = resolve(dataType); - checkAncestry(dataType); - } - - DataTypeComponentDB dtc = null; - try { - - if (flexibleArrayComponent != null) { - flexibleArrayComponent.getDataType().removeParent(this); - componentAdapter.removeRecord(flexibleArrayComponent.getKey()); - flexibleArrayComponent = null; - } - - DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, 0, - -1, -1, name, comment); - dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); - dataType.addParent(this); - flexibleArrayComponent = dtc; - - if (validatePackAndNotify) { - repack(false, false); - compositeAdapter.updateRecord(record, true); - notifySizeChanged(false); - } - } - catch (IOException e) { - dataMgr.dbError(e); - } - return dtc; - } - finally { - lock.release(); - } - } - @Override public void growStructure(int amount) { if (isPackingEnabled()) { @@ -512,7 +536,7 @@ class StructureDB extends CompositeDB implements StructureInternal { OrdinalComparator.INSTANCE); } if (idx >= 0) { - doDelete(idx); // updates timestamp + doDeleteWithComponentShift(idx, false); // updates timestamp } else { // assume non-packed removal of DEFAULT @@ -527,19 +551,44 @@ class StructureDB extends CompositeDB implements StructureInternal { } } - private void doDelete(int index) { + /** + * Removes a defined component at the specified index without + * any alteration to other components. + * @param index defined component index + * @return the defined component which was removed. + * @throws IOException if an IO error occurs + */ + private DataTypeComponentDB doDelete(int index) throws IOException { DataTypeComponentDB dtc = components.remove(index); dtc.getDataType().removeParent(this); + componentAdapter.removeRecord(dtc.getKey()); + return dtc; + } + + /** + * Removes a defined component at the specified index. + * If this corresponds to a zero-length or bit-field component it will + * be cleared without an offset shift to the remaining components. Removal of + * other component types will result in an offset and ordinal shift + * to the remaining components. In the case of a non-packed + * structure, the resulting shift will cause in a timestamp change + * for this structure. + * @param index defined component index + * @param disableOffsetShift if false, and component is not a bit-field, an offset shift + * and possible structure length change will be performed for non-packed structure. + */ + private void doDeleteWithComponentShift(int index, boolean disableOffsetShift) { + DataTypeComponentDB dtc = null; try { - componentAdapter.removeRecord(dtc.getKey()); + dtc = doDelete(index); } catch (IOException e) { - dataMgr.dbError(e); + dataMgr.dbError(e); // does not return } if (isPackingEnabled()) { return; } - int shiftAmount = dtc.isBitFieldComponent() ? 0 : dtc.getLength(); + int shiftAmount = (disableOffsetShift || dtc.isBitFieldComponent()) ? 0 : dtc.getLength(); shiftOffsets(index, -1, -shiftAmount); } @@ -608,7 +657,7 @@ class StructureDB extends CompositeDB implements StructureInternal { } components = newComponents; - updateNumComponents(numComponents + ordinalAdjustment); + updateComposite(numComponents + ordinalAdjustment, -1, -1, true); // update numComponents only if (isPackingEnabled()) { if (!repack(false, true)) { @@ -616,10 +665,10 @@ class StructureDB extends CompositeDB implements StructureInternal { } } else { - structLength += offsetAdjustment; + updateComposite(-1, structLength + offsetAdjustment, -1, true); // update length only if (bitFieldRemoved) { repack(false, false); - } + } notifySizeChanged(false); } } @@ -678,13 +727,10 @@ class StructureDB extends CompositeDB implements StructureInternal { } @Override - public DataTypeComponent getComponent(int ordinal) { + public DataTypeComponentDB getComponent(int ordinal) { lock.acquire(); try { checkIsValid(); - if (ordinal == numComponents && flexibleArrayComponent != null) { - return flexibleArrayComponent; - } if (ordinal < 0 || ordinal >= numComponents) { throw new IndexOutOfBoundsException(ordinal); } @@ -705,9 +751,12 @@ class StructureDB extends CompositeDB implements StructureInternal { else { DataTypeComponent dtc = components.get(idx - 1); offset = dtc.getEndOffset() + ordinal - dtc.getOrdinal(); + if (dtc.getLength() == 0) { + --offset; + } } - - return new DataTypeComponentDB(dataMgr, componentAdapter, this, key, ordinal, offset); + // return undefined component + return new DataTypeComponentDB(dataMgr, this, ordinal, offset); } finally { lock.release(); @@ -766,11 +815,6 @@ class StructureDB extends CompositeDB implements StructureInternal { return struct; } - @Override - public boolean isNotYetDefined() { - return structLength == 0 && flexibleArrayComponent == null; - } - @Override protected int getComputedAlignment(boolean updateRecord) { if (structAlignment > 0) { @@ -812,7 +856,7 @@ class StructureDB extends CompositeDB implements StructureInternal { try { checkIsValid(); if (structLength == 0) { - return 1; // lie about our length if not yet defined + return 1; // positive length required } return structLength; } @@ -838,7 +882,6 @@ class StructureDB extends CompositeDB implements StructureInternal { if (ordinal < 0 || ordinal >= numComponents) { throw new IndexOutOfBoundsException(ordinal); } - int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), OrdinalComparator.INSTANCE); if (idx >= 0) { @@ -870,10 +913,7 @@ class StructureDB extends CompositeDB implements StructureInternal { } /** - * Backup from specified ordinal to the first component which contains the specified offset. For - * normal components the specified ordinal will be returned, however for bit-fields the ordinal - * of the first bit-field containing the specified offset will be returned. - * + * Backup from specified ordinal to the first component which contains the specified offset. * @param index defined component index * @param offset offset within structure * @return index of first defined component containing specific offset. @@ -882,35 +922,28 @@ class StructureDB extends CompositeDB implements StructureInternal { if (index == 0) { return 0; } - DataTypeComponentDB dtc = components.get(index); - while (index != 0 && dtc.isBitFieldComponent()) { + while (index != 0) { DataTypeComponentDB previous = components.get(index - 1); if (!previous.containsOffset(offset)) { break; } - dtc = previous; --index; } return index; } /** - * Advance from specified ordinal to the last component which contains the specified offset. For - * normal components the specified ordinal will be returned, however for bit-fields the ordinal - * of the last bit-field containing the specified offset will be returned. - * + * Advance from specified ordinal to the last component which contains the specified offset. * @param index defined component index * @param offset offset within structure * @return index of last defined component containing specific offset. */ private int advanceToLastComponentContainingOffset(int index, int offset) { - DataTypeComponentDB dtc = components.get(index); - while (index < (components.size() - 1) && dtc.isBitFieldComponent()) { + while (index < (components.size() - 1)) { DataTypeComponentDB next = components.get(index + 1); if (!next.containsOffset(offset)) { break; } - dtc = next; ++index; } return index; @@ -924,26 +957,26 @@ class StructureDB extends CompositeDB implements StructureInternal { if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } - if (offset >= structLength) { + if (offset > structLength) { return; } + int index = Collections.binarySearch(components, Integer.valueOf(offset), OffsetComparator.INSTANCE); - int offsetDelta = 0; - int ordinalDelta = 0; if (index < 0) { - index = -index - 1; - --ordinalDelta; - offsetDelta = -1; - shiftOffsets(index, ordinalDelta, offsetDelta); // updates timestamp + if (offset == structLength) { + return; + } + shiftOffsets(-index - 1, -1, -1); // updates timestamp } else { + // delete all components containing offset working backward from last such component index = advanceToLastComponentContainingOffset(index, offset); DataTypeComponentDB dtc = components.get(index); while (dtc.containsOffset(offset)) { - doDelete(index); // updates timestamp + doDeleteWithComponentShift(index, false); // updates timestamp if (--index < 0) { break; } @@ -957,47 +990,172 @@ class StructureDB extends CompositeDB implements StructureInternal { lock.release(); } } - + @Override - public DataTypeComponent getComponentAt(int offset) { + public void clearAtOffset(int offset) { lock.acquire(); try { - checkIsValid(); - if (offset >= structLength || offset < 0) { - return null; + checkDeleted(); + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); } + if (offset > structLength) { + return; + } + int index = Collections.binarySearch(components, Integer.valueOf(offset), OffsetComparator.INSTANCE); - if (index >= 0) { - DataTypeComponent dtc = components.get(index); - if (dtc.isBitFieldComponent()) { - index = backupToFirstComponentContainingOffset(index, offset); - dtc = components.get(index); + if (index < 0) { + return; + } + + // clear all components containing offset working backward from last such component + index = advanceToLastComponentContainingOffset(index, offset); + DataTypeComponentDB dtc = components.get(index); + while (dtc.containsOffset(offset)) { + doDeleteWithComponentShift(index, true); // updates timestamp + if (--index < 0) { + break; } - return dtc; + dtc = components.get(index); } - else if (isPackingEnabled()) { - return null; - } - index = -index - 1; - int ordinal = offset; - if (index > 0) { - DataTypeComponent dtc = components.get(index - 1); - ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); - } - return new DataTypeComponentDB(dataMgr, componentAdapter, this, key, ordinal, offset); + + repack(false, false); + notifySizeChanged(false); } finally { lock.release(); } } + @Override + public DataTypeComponent getDefinedComponentAtOrAfterOffset(int offset) { + lock.acquire(); + try { + checkIsValid(); + if (offset > structLength || offset < 0) { + return null; + } + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + if (index >= 0) { + DataTypeComponent dtc = components.get(index); + index = backupToFirstComponentContainingOffset(index, offset); + dtc = components.get(index); + return dtc; + } + index = -index - 1; + if (index < components.size()) { + return components.get(index); + } + return null; + } + finally { + lock.release(); + } + } + + @Override + public DataTypeComponent getComponentContaining(int offset) { + lock.acquire(); + try { + checkIsValid(); + if (offset > structLength || offset < 0) { + return null; + } + int index = + Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + if (index >= 0) { + // return first matching defined component containing offset + DataTypeComponent dtc = components.get(index); + index = backupToFirstComponentContainingOffset(index, offset); + dtc = components.get(index); + return dtc; + } + + if (offset != structLength && !isPackingEnabled()) { + // return undefined component for padding offset within non-packed structure + return generateUndefinedComponent(offset, index); + } + return null; + } + finally { + lock.release(); + } + } + + @Override + public List getComponentsContaining(int offset) { + lock.acquire(); + try { + checkIsValid(); + ArrayList list = new ArrayList<>(); + if (offset > structLength || offset < 0) { + return list; + } + int index = + Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + boolean hasSizedComponent = false; + if (index >= 0) { + // collect matching defined components containing offset + DataTypeComponentDB dtc = components.get(index); + index = backupToFirstComponentContainingOffset(index, offset); + while (index < components.size()) { + dtc = components.get(index); + if (!dtc.containsOffset(offset)) { + break; + } + ++index; + hasSizedComponent |= (dtc.getLength() != 0); + list.add(dtc); + } + // transform index for use with generateUndefinedComponent if invoked + index = -index - 1; + } + + if (!hasSizedComponent && offset != structLength && !isPackingEnabled()) { + // generate undefined componentfor padding offset within non-packed structure + // if offset only occupied by zero-length component + list.add(generateUndefinedComponent(offset, index)); + } + return list; + } + finally { + lock.release(); + } + } + + /** + * Generate an undefined component following a binary search across the defined components. + * @param offset the offset within this structure which was searched for + * @param missingComponentIndex the defined component binary search index result (must be negative) + * @return undefined component + */ + private DataTypeComponentDB generateUndefinedComponent(int offset, int missingComponentIndex) { + if (missingComponentIndex >= 0) { + throw new AssertException(); + } + missingComponentIndex = -missingComponentIndex - 1; + int ordinal = offset; + if (missingComponentIndex > 0) { + // compute ordinal from previous defined component + DataTypeComponent dtc = components.get(missingComponentIndex - 1); + ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); + if (dtc.getLength() == 0) { + ordinal += 1; + } + } + return new DataTypeComponentDB(dataMgr, this, ordinal, offset); + } + @Override public DataTypeComponent getDataTypeAt(int offset) { lock.acquire(); try { - DataTypeComponent dtc = getComponentAt(offset); + DataTypeComponent dtc = getComponentContaining(offset); if (dtc != null) { DataType dt = dtc.getDataType(); if (dt instanceof Structure) { @@ -1060,7 +1218,7 @@ class StructureDB extends CompositeDB implements StructureInternal { checkAncestry(dataType); if ((offset > structLength) && !isPackingEnabled()) { - numComponents = numComponents + (offset - structLength); + numComponents += offset - structLength; structLength = offset; } @@ -1089,8 +1247,7 @@ class StructureDB extends CompositeDB implements StructureInternal { shiftOffsets(index, 1 + additionalShift, 1 + additionalShift); repack(false, false); notifySizeChanged(false); - return new DataTypeComponentDB(dataMgr, componentAdapter, this, key, ordinal, - offset); + return new DataTypeComponentDB(dataMgr, this, ordinal, offset); } length = getPreferredComponentLength(dataType, length); @@ -1099,7 +1256,7 @@ class StructureDB extends CompositeDB implements StructureInternal { ordinal, offset, name, comment); dataType.addParent(this); DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); - shiftOffsets(index, 1 + additionalShift, dtc.getLength() + additionalShift); + shiftOffsets(index, 1 + additionalShift, length + additionalShift); components.add(index, dtc); repack(false, false); notifySizeChanged(false); @@ -1118,7 +1275,14 @@ class StructureDB extends CompositeDB implements StructureInternal { } @Override - public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name, + public final DataTypeComponent replace(int ordinal, DataType dataType, int length) + throws IllegalArgumentException { + return replace(ordinal, dataType, length, null, null); + } + + @Override + public DataTypeComponent replace(int ordinal, DataType dataType, int length, + String componentName, String comment) { lock.acquire(); try { @@ -1130,31 +1294,93 @@ class StructureDB extends CompositeDB implements StructureInternal { dataType = validateDataType(dataType); - DataTypeComponent origDtc = getComponent(ordinal); - if (origDtc.isBitFieldComponent()) { - throw new IllegalArgumentException( - "Bit-field component may not be directly replaced"); - } - - if (dataType == DataType.DEFAULT) { - clearComponent(ordinal); - return getComponent(ordinal); - } - dataType = resolve(dataType); checkAncestry(dataType); length = getPreferredComponentLength(dataType, length); + LinkedList replacedComponents = new LinkedList<>(); + int offset; + + int index = ordinal; + if (!isPackingEnabled()) { + index = Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); + } + if (index >= 0) { + // defined component + DataTypeComponentDB origDtc = components.get(index); + offset = origDtc.getOffset(); + + if (isPackingEnabled() || length == 0) { + // case 1: packed structure or zero-length replacement - do 1-for-1 replacement + replacedComponents.add(origDtc); + } + else if (origDtc.getLength() == 0) { + // case 2: replaced component is zero-length (like-for-like replacement handled by case 1 above + throw new IllegalArgumentException( + "Zero-length component may only be replaced with another zero-length component"); + } + else if (origDtc.isBitFieldComponent()) { + // case 3: replacing bit-field (must replace all bit-fields which overlap) + int minOffset = origDtc.getOffset(); + int maxOffset = origDtc.getEndOffset(); + + replacedComponents.add(origDtc); + + // consume bit-field overlaps before + for (int i = index - 1; i >= 0; i--) { + origDtc = components.get(i); + if (origDtc.getLength() == 0 || !origDtc.containsOffset(minOffset)) { + break; + } + replacedComponents.add(0, origDtc); + } + + // consume bit-field overlaps after + for (int i = index + 1; i < components.size(); i++) { + origDtc = components.get(i); + if (origDtc.getLength() == 0 || !origDtc.containsOffset(maxOffset)) { + break; + } + replacedComponents.add(origDtc); + } + } + else { + // case 4: sized component replacemnt - do 1-for-1 replacement + replacedComponents.add(origDtc); + } + } + else { + // case 5: undefined component replaced (non-packed only) + index = -index - 1; + offset = ordinal; + if (index > 0) { + // use previous defined component to compute undefined offset + DataTypeComponent dtc = components.get(index - 1); + offset = dtc.getEndOffset() + ordinal - dtc.getOrdinal(); + if (dtc.getLength() == 0) { + --offset; + } + } + DataTypeComponentDB origDtc = + new DataTypeComponentDB(dataMgr, this, ordinal, offset); + if (dataType == DataType.DEFAULT) { + return origDtc; // no change + } + replacedComponents.add(origDtc); + } + DataTypeComponent replaceComponent = - replaceComponent(origDtc, dataType, length, name, comment); + replaceComponents(replacedComponents, dataType, offset, length, componentName, + comment); repack(false, false); record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); compositeAdapter.updateRecord(record, true); notifySizeChanged(false); - return replaceComponent; + return replaceComponent != null ? replaceComponent : getComponent(ordinal); } catch (DataTypeDependencyException e) { throw new IllegalArgumentException(e.getMessage(), e); @@ -1168,55 +1394,99 @@ class StructureDB extends CompositeDB implements StructureInternal { } } - @Override - public final DataTypeComponent replace(int ordinal, DataType dataType, int length) - throws IllegalArgumentException { - return replace(ordinal, dataType, length, null, null); - } - @Override public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name, String comment) throws IllegalArgumentException { if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } - if (offset >= getLength()) { - throw new IllegalArgumentException( - "Offset " + offset + " is beyond end of structure (" + structLength + ")."); - } - + lock.acquire(); try { checkDeleted(); + + if (offset >= structLength) { + throw new IllegalArgumentException( + "Offset " + offset + " is beyond end of structure (" + structLength + ")."); + } dataType = validateDataType(dataType); - - DataTypeComponent origDtc = getComponentAt(offset); - if (origDtc.isBitFieldComponent()) { - throw new IllegalArgumentException( - "Bit-field component may not be directly replaced"); - } - - if (dataType == DataType.DEFAULT) { - int ordinal = origDtc.getOrdinal(); - clearComponent(ordinal); - return getComponent(ordinal); - } - dataType = resolve(dataType); checkAncestry(dataType); + LinkedList replacedComponents = new LinkedList<>(); + + DataTypeComponentDB origDtc = null; + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + if (index >= 0) { + + // defined component found - advance to last one containing offset + index = advanceToLastComponentContainingOffset(index, offset); + origDtc = components.get(index); + + // case 1: only defined component(s) at offset are zero-length + if (origDtc.getLength() == 0) { + if (isPackingEnabled()) { + // if packed: insert after zero-length component + return insert(index + 1, dataType, length, name, comment); + } + // if non-packed: replace undefined component which immediately follows the zero-length component + replacedComponents.add( + new DataTypeComponentDB(dataMgr, this, origDtc.getOrdinal() + 1, offset)); + } + + // case 2: sized component at offset is bit-field (must replace all bit-fields which contain offset) + else if (origDtc.isBitFieldComponent()) { + replacedComponents.add(origDtc); + for (int i = index - 1; i >= 0; i--) { + origDtc = components.get(i); + if (origDtc.getLength() == 0 || !origDtc.containsOffset(offset)) { + break; + } + replacedComponents.add(0, origDtc); + } + } + + // case 3: normal replacement of sized component + else { + replacedComponents.add(origDtc); + } + } + else { + // defined component not found + index = -index - 1; + + if (isPackingEnabled()) { + // case 4: if replacing padding for packed struction perform insert at correct ordinal + return insert(index, dataType, length, name, comment); + } + + // case 5: replace undefined component at offset - compute undefined component to be replaced + int ordinal = offset; + if (index > 0) { + // use previous defined component to determine ordinal for undefined component + DataTypeComponent dtc = components.get(index - 1); + ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); + } + origDtc = new DataTypeComponentDB(dataMgr, this, ordinal, offset); + if (dataType == DataType.DEFAULT) { + return origDtc; // no change + } + replacedComponents.add(origDtc); + } + length = getPreferredComponentLength(dataType, length); DataTypeComponent replaceComponent = - replaceComponent(origDtc, dataType, length, name, comment); + replaceComponents(replacedComponents, dataType, offset, length, name, comment); repack(false, false); record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); compositeAdapter.updateRecord(record, true); notifySizeChanged(false); - return replaceComponent; + return replaceComponent != null ? replaceComponent : getComponentContaining(offset); } catch (DataTypeDependencyException e) { throw new IllegalArgumentException(e.getMessage(), e); @@ -1278,32 +1548,16 @@ class StructureDB extends CompositeDB implements StructureInternal { throws DataTypeDependencyException, IOException { // pre-resolved component types to catch dependency issues early - DataTypeComponent flexComponent = struct.getFlexibleArrayComponent(); DataTypeComponent[] otherComponents = struct.getDefinedComponents(); DataType[] resolvedDts = new DataType[otherComponents.length]; for (int i = 0; i < otherComponents.length; i++) { resolvedDts[i] = doCheckedResolve(otherComponents[i].getDataType()); } - DataType resolvedFlexDt = null; - if (flexComponent != null) { - resolvedFlexDt = doCheckedResolve(flexComponent.getDataType()); - if (isInvalidFlexArrayDataType(resolvedFlexDt)) { - throw new IllegalArgumentException( - "Unsupported flexType: " + resolvedFlexDt.getDisplayName()); - } - } - for (DataTypeComponentDB dtc : components) { dtc.getDataType().removeParent(this); componentAdapter.removeRecord(dtc.getKey()); } - if (flexibleArrayComponent != null) { - flexibleArrayComponent.getDataType().removeParent(this); - componentAdapter.removeRecord(flexibleArrayComponent.getKey()); - flexibleArrayComponent = null; - } - components.clear(); numComponents = 0; structLength = 0; @@ -1313,15 +1567,10 @@ class StructureDB extends CompositeDB implements StructureInternal { doSetPackingAndAlignment(struct); // updates timestamp if (struct.isPackingEnabled()) { - doReplaceWithAligned(struct, resolvedDts); + doReplaceWithPacked(struct, resolvedDts); } else { - doReplaceWithUnaligned(struct, resolvedDts); - } - - if (flexComponent != null) { - doAddFlexArray(resolvedFlexDt, flexComponent.getFieldName(), flexComponent.getComment(), - false); + doReplaceWithNonPacked(struct, resolvedDts); } repack(false, false); @@ -1341,7 +1590,7 @@ class StructureDB extends CompositeDB implements StructureInternal { } } - private void doReplaceWithAligned(Structure struct, DataType[] resolvedDts) { + private void doReplaceWithPacked(Structure struct, DataType[] resolvedDts) { // assumes components is clear and that alignment characteristics have been set DataTypeComponent[] otherComponents = struct.getDefinedComponents(); for (int i = 0; i < otherComponents.length; i++) { @@ -1357,14 +1606,14 @@ class StructureDB extends CompositeDB implements StructureInternal { } } - private void doReplaceWithUnaligned(Structure struct, DataType[] resolvedDts) + private void doReplaceWithNonPacked(Structure struct, DataType[] resolvedDts) throws IOException { // assumes components is clear and that alignment characteristics have been set. - if (struct.isZeroLength()) { + if (struct.isNotYetDefined()) { return; } - structLength = struct.getLength(); + structLength = struct.isZeroLength() ? 0 : struct.getLength(); numComponents = structLength; DataTypeComponent[] otherComponents = struct.getDefinedComponents(); @@ -1372,9 +1621,9 @@ class StructureDB extends CompositeDB implements StructureInternal { DataTypeComponent dtc = otherComponents[i]; DataType dt = resolvedDts[i]; // ancestry check already performed by caller - - int length = dt.getLength(); - if (length <= 0 || dtc.isBitFieldComponent()) { + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length < 0 || dtc.isBitFieldComponent()) { + // TODO: bitfield truncation/expansion may be an issues if data organization changes length = dtc.getLength(); } else { @@ -1385,9 +1634,11 @@ class StructureDB extends CompositeDB implements StructureInternal { maxOffset = otherComponents[nextIndex].getOffset(); } else { - maxOffset = struct.getLength(); + maxOffset = structLength; + } + if (length > 0) { + length = Math.min(length, maxOffset - dtc.getOffset()); } - length = Math.min(length, maxOffset - dtc.getOffset()); } DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dt), key, length, @@ -1400,40 +1651,12 @@ class StructureDB extends CompositeDB implements StructureInternal { repack(false, false); } - @Override - protected void postPointerResolve(DataType definitionDt, DataTypeConflictHandler handler) { - - Structure struct = (Structure) definitionDt; - if (struct.hasFlexibleArrayComponent() != hasFlexibleArrayComponent()) { - throw new IllegalArgumentException("mismatched definition datatype"); - } - - super.postPointerResolve(definitionDt, handler); - - if (flexibleArrayComponent != null) { - DataTypeComponent flexDtc = struct.getFlexibleArrayComponent(); - DataType dt = flexDtc.getDataType(); - if (dt instanceof Pointer) { - flexibleArrayComponent.getDataType().removeParent(this); - dt = dataMgr.resolve(dt, handler); - flexibleArrayComponent.setDataType(dt); - dt.addParent(this); - } - } - } - @Override public void dataTypeDeleted(DataType dt) { lock.acquire(); try { checkDeleted(); boolean changed = false; - if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == dt) { - flexibleArrayComponent.getDataType().removeParent(this); - componentAdapter.removeRecord(flexibleArrayComponent.getKey()); - flexibleArrayComponent = null; - changed = true; - } int n = components.size(); for (int i = n - 1; i >= 0; i--) { DataTypeComponentDB dtc = components.get(i); @@ -1487,8 +1710,8 @@ class StructureDB extends CompositeDB implements StructureInternal { if (dtc.getDataType() == dt) { // assume no impact to bitfields since base types should not change size int dtcLen = dtc.getLength(); - int length = dt.getLength(); - if (length <= 0) { + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length < 0) { length = dtcLen; } if (length < dtcLen) { @@ -1537,15 +1760,18 @@ class StructureDB extends CompositeDB implements StructureInternal { for (int i = 0; i < n; i++) { DataTypeComponentDB dtc = components.get(i); DataType dt = dtc.getDataType(); + if (dt instanceof Dynamic) { + continue; // length can't change + } if (dt instanceof BitFieldDataType) { // TODO: could get messy continue; } - int dtcLen = dtc.getLength(); - int length = dt.getLength(); - if (length <= 0) { - length = dtcLen; + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length < 0) { + continue; // illegal condition - skip } + int dtcLen = dtc.getLength(); if (dtcLen != length) { if (isPacked) { dtc.setLength(length, true); @@ -1627,17 +1853,6 @@ class StructureDB extends CompositeDB implements StructureInternal { return false; } - DataTypeComponent myFlexComp = getFlexibleArrayComponent(); - DataTypeComponent otherFlexComp = struct.getFlexibleArrayComponent(); - if (myFlexComp != null) { - if (otherFlexComp == null || !myFlexComp.isEquivalent(otherFlexComp)) { - return false; - } - } - else if (otherFlexComp != null) { - return false; - } - int myNumComps = components.size(); int otherNumComps = struct.getNumDefinedComponents(); if (myNumComps != otherNumComps) { @@ -1694,7 +1909,7 @@ class StructureDB extends CompositeDB implements StructureInternal { return available; } - private int getLastDefinedComponentIndex() { + private int getLastDefinedComponentOrdinal() { if (components.size() == 0) { return 0; } @@ -1728,69 +1943,137 @@ class StructureDB extends CompositeDB implements StructureInternal { } /** - * Replace the indicated component with a new component containing the specified data type. - * Flex-array component not handled. + * Check for available undefined bytes within a non-packed structure for a component + * update with the specified ordinal. + * @param lastOrdinalReplacedOrUpdated the ordinal of a component to be updated + * or the last ordinal with in a sequence of components being replaced. + * @param bytesNeeded number of additional bytes required to complete operation + * @throws IllegalArgumentException if unable to identify/make sufficient space + */ + private void checkUndefinedSpaceAvailabilityAfter(int lastOrdinalReplacedOrUpdated, + int bytesNeeded, DataType newDataType, int offset) throws IllegalArgumentException { + if (bytesNeeded <= 0) { + return; + } + int bytesAvailable = getNumUndefinedBytes(lastOrdinalReplacedOrUpdated + 1); + if (bytesAvailable < bytesNeeded) { + if (lastOrdinalReplacedOrUpdated == getLastDefinedComponentOrdinal()) { + growStructure(bytesNeeded - bytesAvailable); + } + else { + throw new IllegalArgumentException("Not enough undefined bytes to fit " + + newDataType.getPathName() + " in structure " + getPathName() + " at offset 0x" + + Integer.toHexString(offset) + "." + " It needs " + + (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."); + } + } + } + + /** + * Replace the specified components with a new component containing the specified data type. + * If {@link DataType#DEFAULT} is specified as the resolvedDataType only a clear operation + * is performed. * - * @param origDtc the original data type component in this structure. + * @param origComponents the original sequence of data type components in this structure + * to be replaced. These components must be adjacent components in sequential order. + * If an non-packed undefined component is specified no other component may be included. * @param resolvedDataType the data type of the new component + * @param newOffset offset of replacement component which must fall within origComponents bounds * @param length the length of the new component * @param name the field name of the new component * @param comment the comment for the new component - * @return the new component or null if the new component couldn't fit. + * @return the new component or null if only a clear operation was performed. * @throws IOException if database IO error occurs + * @throws IllegalArgumentException if unable to identify/make sufficient space */ - private DataTypeComponent replaceComponent(DataTypeComponent origDtc, DataType resolvedDataType, - int length, String name, String comment) throws IOException { + private DataTypeComponent replaceComponents(LinkedList origComponents, + DataType resolvedDataType, int newOffset, int length, String name, String comment) + throws IOException, IllegalArgumentException { -// FIXME: Unsure how to support replace operation with bit-fields. Within non-packed structure -// the packing behavior for bit-fields prevents a one-for-one replacement and things may shift -// around which the non-packed structure tries to avoid. Insert and delete are less of a concern -// since movement already can occur, although insert at offset may not retain the offset if it -// interacts with bit-fields. + boolean clearOnly = false; + if (resolvedDataType == DataType.DEFAULT) { + clearOnly = true; + length = 0; // nothing gets consumed + } - int ordinal = origDtc.getOrdinal(); - int newOffset = origDtc.getOffset(); - int dtcLength = origDtc.getLength(); - int bytesNeeded = length - dtcLength; - int deltaOrdinal = -bytesNeeded; - if (!isPackingEnabled() && bytesNeeded > 0) { - int bytesAvailable = getNumUndefinedBytes(ordinal + 1); - if (bytesAvailable < bytesNeeded) { - if (ordinal == getLastDefinedComponentIndex()) { - growStructure(bytesNeeded - bytesAvailable); + DataTypeComponentDB origFirstDtc = origComponents.getFirst(); + DataTypeComponentDB origLastDtc = origComponents.getLast(); + int origFirstOrdinal = origFirstDtc.getOrdinal(); + int origLastOrdinal = origLastDtc.getOrdinal(); + int minReplacedOffset = origFirstDtc.getOffset(); + int maxReplacedOffset = origLastDtc.getEndOffset(); + + // Perform origComponents checks + if (newOffset < minReplacedOffset || newOffset > maxReplacedOffset) { + throw new AssertException("newOffset not contained within origComponents"); + } + if (origComponents.size() > 1) { + int checkOrdinal = origFirstOrdinal; + for (DataTypeComponentDB origDtc : origComponents) { + if (origDtc.isUndefined()) { + throw new AssertException( + "undefined component within multi-component sequence"); } - else { - throw new IllegalArgumentException("Not enough undefined bytes to fit " + - resolvedDataType.getPathName() + " in structure " + getPathName() + - " at offset 0x" + Integer.toHexString(newOffset) + "." + " It needs " + - (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."); + if (origDtc.getOrdinal() != checkOrdinal++) { + throw new AssertException("non-sequential components specified"); } } } - DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(resolvedDataType), key, - length, ordinal, newOffset, name, comment); - resolvedDataType.addParent(this); - DataTypeComponentDB newDtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); + + int leadingUnusedBytes = newOffset - minReplacedOffset; + int newOrdinal = origFirstOrdinal; + if (!isPackingEnabled()) { + newOrdinal += leadingUnusedBytes; // leading unused bytes will become undefined components + } + + // compute space freed by component removal + int origLength = 0; + if (origLastDtc.getLength() != 0) { + origLength = maxReplacedOffset - minReplacedOffset + 1; + } + + if (!clearOnly && !isPackingEnabled()) { + int bytesNeeded = length - origLength + leadingUnusedBytes; + checkUndefinedSpaceAvailabilityAfter(origLastOrdinal, bytesNeeded, resolvedDataType, + newOffset); + } + + // determine defined component list insertion point, remove old components + // and insert new component in list int index; if (isPackingEnabled()) { - index = ordinal; + index = newOrdinal; } else { index = - Collections.binarySearch(components, Integer.valueOf(ordinal), + Collections.binarySearch(components, Integer.valueOf(origFirstOrdinal), OrdinalComparator.INSTANCE); } if (index < 0) { - index = -index - 1; + index = -index - 1; // undefined component replacement } else { - DataTypeComponentDB dataTypeComponentDB = components.get(index); // TODO Remove this. - dataTypeComponentDB.getDataType().removeParent(this); - DataTypeComponentDB dtc = components.remove(index); - componentAdapter.removeRecord(dtc.getKey()); + for (DataTypeComponentDB origDtc : origComponents) { + DataTypeComponentDB dtc = doDelete(index); + if (dtc != origDtc) { + throw new AssertException("component replacement mismatch"); + } + } } - components.add(index, newDtc); - if (deltaOrdinal != 0) { + + DataTypeComponentDB newDtc = null; + if (!clearOnly) { + // insert new component + DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(resolvedDataType), + key, length, newOrdinal, newOffset, name, comment); + resolvedDataType.addParent(this); + newDtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); + components.add(index, newDtc); + } + + // adjust ordinals of trailing components - defer if packing is enabled + if (!isPackingEnabled()) { + int deltaOrdinal = -origComponents.size() + origLength - length; shiftOffsets(index + 1, deltaOrdinal, 0); } return newDtc; @@ -1840,34 +2123,17 @@ class StructureDB extends CompositeDB implements StructureInternal { DataType replacementDt = newDt; try { validateDataType(replacementDt); - if (!(replacementDt instanceof DataTypeDB) || - (replacementDt.getDataTypeManager() != dataMgr)) { - replacementDt = resolve(replacementDt); - } + replacementDt = resolve(replacementDt); checkAncestry(replacementDt); } catch (Exception e) { // 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 (bitfield, aligned structure, etc.) // TODO: failing silently is rather hidden replacementDt = DataType.DEFAULT; } - boolean changed = false; - if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == oldDt) { - flexibleArrayComponent.getDataType().removeParent(this); - if (isInvalidFlexArrayDataType(replacementDt)) { - componentAdapter.removeRecord(flexibleArrayComponent.getKey()); - flexibleArrayComponent = null; - Msg.error(this, "Invalid flex array replacement type " + newDt.getName() + - ", removing flex array: " + getPathName()); - } - else { - flexibleArrayComponent.setDataType(replacementDt); - replacementDt.addParent(this); - } - changed = true; - } + boolean changed = false; for (int i = components.size() - 1; i >= 0; i--) { DataTypeComponentDB comp = components.get(i); @@ -1911,7 +2177,7 @@ class StructureDB extends CompositeDB implements StructureInternal { if (changed) { repack(false, false); compositeAdapter.updateRecord(record, true); // update timestamp - notifySizeChanged(false); + notifySizeChanged(false); // also handles alignment change } } catch (IOException e) { @@ -1922,12 +2188,12 @@ class StructureDB extends CompositeDB implements StructureInternal { } } - private void setComponentDataType(DataTypeComponentDB comp, DataType replacementDt, + private void setComponentDataType(DataTypeComponentDB comp, DataType newDt, int nextIndex) { int oldLen = comp.getLength(); - int len = replacementDt.getLength(); - if (len < 1) { + int len = DataTypeComponent.usesZeroLengthComponent(newDt) ? 0 : newDt.getLength(); + if (len < 0) { len = oldLen; } @@ -1936,8 +2202,8 @@ class StructureDB extends CompositeDB implements StructureInternal { if (isPackingEnabled()) { comp.setLength(len, false); // do before record save below } - comp.setDataType(replacementDt); // saves component record - replacementDt.addParent(this); + comp.setDataType(newDt); // saves component record + newDt.addParent(this); if (isPackingEnabled()) { return; @@ -1954,7 +2220,7 @@ class StructureDB extends CompositeDB implements StructureInternal { comp.setLength(len, true); shiftOffsets(nextIndex, -bytesNeeded, 0); } - else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { + else if (comp.getOrdinal() == getLastDefinedComponentOrdinal()) { // we are the last defined component, grow structure doGrowStructure(bytesNeeded - bytesAvailable); comp.setLength(len, true); @@ -1977,13 +2243,6 @@ class StructureDB extends CompositeDB implements StructureInternal { lock.acquire(); try { checkDeleted(); - - if (flexibleArrayComponent != null) { - flexibleArrayComponent.getDataType().removeParent(this); - componentAdapter.removeRecord(flexibleArrayComponent.getKey()); - flexibleArrayComponent = null; - } - for (DataTypeComponentDB dtc : components) { dtc.getDataType().removeParent(this); try { @@ -2085,40 +2344,21 @@ class StructureDB extends CompositeDB implements StructureInternal { return changed; } - private boolean updateNumComponents(int currentNumComponents) { - boolean compositeChanged = false; - if (numComponents != currentNumComponents) { - numComponents = currentNumComponents; - record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); - compositeChanged = true; - } - if (compositeChanged) { - try { - compositeAdapter.updateRecord(record, true); - return true; - } - catch (IOException e) { - dataMgr.dbError(e); - } - } - return false; - } - private boolean updateComposite(int currentNumComponents, int currentLength, int currentAlignment, boolean setLastChangeTime) { boolean compositeChanged = false; - if (numComponents != currentNumComponents) { + if (currentNumComponents >= 0 && numComponents != currentNumComponents) { numComponents = currentNumComponents; record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); setLastChangeTime = true; compositeChanged = true; } - if (structLength != currentLength) { + if (currentLength >= 0 && structLength != currentLength) { structLength = currentLength; record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); compositeChanged = true; } - if (structAlignment != currentAlignment) { + if (currentAlignment >= 0 && structAlignment != currentAlignment) { structAlignment = currentAlignment; record.setIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL, structAlignment); compositeChanged = true; @@ -2135,65 +2375,4 @@ class StructureDB extends CompositeDB implements StructureInternal { return false; } - @Override - public boolean hasFlexibleArrayComponent() { - return flexibleArrayComponent != null; - } - - @Override - public DataTypeComponentDB getFlexibleArrayComponent() { - return flexibleArrayComponent; - } - - private boolean isInvalidFlexArrayDataType(DataType dataType) { - return (dataType == null || dataType == DataType.DEFAULT || - dataType instanceof BitFieldDataType || dataType instanceof Dynamic || - dataType instanceof FactoryDataType); - } - - @Override - public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, - String comment) throws IllegalArgumentException { - if (isInvalidFlexArrayDataType(flexType)) { - throw new IllegalArgumentException( - "Unsupported flexType: " + flexType.getDisplayName()); - } - try { - return doAddFlexArray(flexType, name, comment, true); - } - catch (DataTypeDependencyException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - @Override - public void clearFlexibleArrayComponent() { - lock.acquire(); - try { - checkDeleted(); - if (flexibleArrayComponent == null) { - return; - } - - DataTypeComponentDB dtc = flexibleArrayComponent; - flexibleArrayComponent = null; - dtc.getDataType().removeParent(this); - try { - componentAdapter.removeRecord(dtc.getKey()); - } - catch (IOException e) { - dataMgr.dbError(e); - } - repack(false, false); - compositeAdapter.updateRecord(record, true); - notifySizeChanged(false); - } - catch (IOException e) { - dataMgr.dbError(e); - } - finally { - lock.release(); - } - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java index 40d0513726..9ca2cc1140 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java @@ -55,11 +55,6 @@ class TypedefDB extends DataTypeDB implements TypeDef { return record.getString(TypedefDBAdapter.TYPEDEF_NAME_COL); } - @Override - public boolean isNotYetDefined() { - return getDataType().isNotYetDefined(); - } - @Override public boolean hasLanguageDependantLength() { return getDataType().hasLanguageDependantLength(); @@ -209,7 +204,6 @@ class TypedefDB extends DataTypeDB implements TypeDef { lock.acquire(); try { if (checkIsValid() && getDataType() == oldDt) { - int oldLen = getLength(); oldDt.removeParent(this); newDt = resolve(newDt); newDt.addParent(this); @@ -221,9 +215,12 @@ class TypedefDB extends DataTypeDB implements TypeDef { catch (IOException e) { dataMgr.dbError(e); } - if (oldLen != getLength()) { + if (oldDt.getLength() != newDt.getLength()) { notifySizeChanged(false); } + else if (oldDt.getAlignment() != newDt.getAlignment()) { + notifyAlignmentChanged(false); + } else { dataMgr.dataTypeChanged(this, false); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java index 1d151df09f..da516ddd99 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java @@ -81,11 +81,6 @@ class UnionDB extends CompositeDB implements UnionInternal { return ""; } - @Override - public boolean isNotYetDefined() { - return components.size() == 0; - } - @Override public DataTypeComponent add(DataType dataType, int length, String componentName, String comment) throws IllegalArgumentException { @@ -462,7 +457,7 @@ class UnionDB extends CompositeDB implements UnionInternal { try { checkIsValid(); if (unionLength == 0) { - return 1; // lie about our length + return 1; // positive length required } return unionLength; } @@ -482,15 +477,17 @@ class UnionDB extends CompositeDB implements UnionInternal { boolean changed = false; for (DataTypeComponentDB dtc : components) { DataType dt = dtc.getDataType(); + if (dt instanceof Dynamic) { + continue; // length can't change + } if (dt instanceof BitFieldDataType) { dt = adjustBitField(dt); // in case base type changed } - int dtcLen = dtc.getLength(); - int length = dt.getLength(); - if (length <= 0) { - length = dtcLen; + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length < 0) { + continue; // illegal condition - skip } - if (length != dtcLen) { + if (length != dtc.getLength()) { dtc.setLength(length, true); changed = true; } @@ -534,12 +531,11 @@ class UnionDB extends CompositeDB implements UnionInternal { boolean changed = false; for (DataTypeComponentDB dtc : components) { if (dtc.getDataType() == dt) { - int length = dt.getLength(); - if (length <= 0) { - length = dtc.getLength(); + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length >= 0 && length != dtc.getLength()) { + dtc.setLength(length, true); + changed = true; } - dtc.setLength(length, true); - changed = true; } } if (changed && !repack(true, true)) { @@ -826,13 +822,15 @@ class UnionDB extends CompositeDB implements UnionInternal { remove = true; } else { - oldDt.removeParent(this); - dtc.setDataType(replacementDt); - replacementDt.addParent(this); - int len = replacementDt.getLength(); - if (len > 0) { - dtc.setLength(len, true); + int len = DataTypeComponent.usesZeroLengthComponent(newDt) ? 0 + : newDt.getLength(); + if (len < 0) { + len = dtc.getLength(); } + oldDt.removeParent(this); + dtc.setLength(len, false); + dtc.setDataType(replacementDt); // updates record + replacementDt.addParent(this); changed = true; } } @@ -844,10 +842,15 @@ class UnionDB extends CompositeDB implements UnionInternal { changed = true; } } - if (changed && !repack(false, true)) { - dataMgr.dataTypeChanged(this, false); + if (changed) { + repack(false, false); + compositeAdapter.updateRecord(record, true); // update timestamp + notifySizeChanged(false); // also handles alignment change } } + catch (IOException e) { + dataMgr.dbError(e); + } finally { lock.release(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/MemReferenceDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/MemReferenceDB.java index 81357296c8..7858f56c4a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/MemReferenceDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/MemReferenceDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +19,7 @@ import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; import ghidra.program.util.*; +import ghidra.util.task.TaskMonitor; class MemReferenceDB extends ReferenceDB { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java index bacc9f3872..4fb38fcbcc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.symbol; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java index de79b7d975..d40944165b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.symbol; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AndQuery.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AndQuery.java index 9d6d95786d..197e0e48ae 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AndQuery.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AndQuery.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; import db.DBRecord; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseVersionException.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseVersionException.java index c22d5c1f04..9ec1c5c826 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseVersionException.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseVersionException.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/EmptyRecordIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/EmptyRecordIterator.java index d5f4d02d6b..3ea21042bf 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/EmptyRecordIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/EmptyRecordIterator.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; import java.io.IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/FieldRangeQuery.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/FieldRangeQuery.java index 961c15b070..eca18ff310 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/FieldRangeQuery.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/FieldRangeQuery.java @@ -13,13 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; -import db.Field; import db.DBRecord; +import db.Field; /** * Query implementation used to test a field in a record to fall within a range of values. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/NotQuery.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/NotQuery.java index eea151fd5c..9955146237 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/NotQuery.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/NotQuery.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; import db.DBRecord; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/OrQuery.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/OrQuery.java index 69ac6040da..13cc349bbc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/OrQuery.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/OrQuery.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; import db.DBRecord; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/Query.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/Query.java index 9d05b4e21e..2d49dea8ed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/Query.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/Query.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; import db.DBRecord; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/QueryRecordIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/QueryRecordIterator.java index b08eb7119d..1337e13211 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/QueryRecordIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/QueryRecordIterator.java @@ -13,18 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; -import ghidra.util.Msg; -import ghidra.util.exception.ClosedException; - import java.io.IOException; import db.DBRecord; import db.RecordIterator; +import ghidra.util.Msg; +import ghidra.util.exception.ClosedException; /** * Iterator that only returns records from another iterator that match the given query. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/StringMatchQuery.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/StringMatchQuery.java index 4cbf2feedc..172ec5d4f4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/StringMatchQuery.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/StringMatchQuery.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.util; import java.util.regex.Matcher; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressIterator.java index bc841c14f2..47b233e1f0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.model.address; import java.util.Iterator; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressRangeIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressRangeIterator.java index 16e6833bc7..79ea596783 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressRangeIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/EmptyAddressRangeIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.model.address; import java.util.Iterator; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java index b914ec98f0..d7aec5563c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java @@ -124,6 +124,7 @@ public abstract class AbstractDataType implements DataType { @Override public boolean isDeleted() { + // NOTE: Support for this concept outside of DataTypeDB should not be relied upon return false; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java index cd79ff9e9e..06895671b5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java @@ -54,12 +54,12 @@ class AlignedComponentPacker { /** * Add next component within structure. All components should be added in sequence excluding - * DEFAULT components and any trailing flexible array component. + * DEFAULT components. * @param dtc structure component * @param isLastComponent true if dtc is the last component within the structure */ void addComponent(InternalDataTypeComponent dtc, boolean isLastComponent) { - if (dtc.getDataType() == DataType.DEFAULT || dtc.isFlexibleArrayComponent()) { + if (dtc.getDataType() == DataType.DEFAULT) { throw new IllegalArgumentException("unsupported component"); } if (!packComponent(dtc)) { @@ -81,8 +81,6 @@ class AlignedComponentPacker { /** * Get the external structure alignment after all components have been packed. - * NOTE: This value does not factor in the affects of any trailing flexible array component - * which may exist or the alignment which may have been explicitly set on the structure. * @return external alignment */ int getDefaultAlignment() { @@ -138,7 +136,7 @@ class AlignedComponentPacker { } private boolean isIgnoredZeroBitField(BitFieldDataType zeroBitFieldDt) { - if (!zeroBitFieldDt.isZeroLengthField()) { + if (!zeroBitFieldDt.isZeroLength()) { return false; } if (bitFieldPacking.useMSConvention()) { @@ -202,15 +200,9 @@ class AlignedComponentPacker { } if (isLastComponent) { - // special handling of zero-length bitfield when it is last component, - // place on last byte within structure. - int offset = groupOffset; - int length = 1; - if (lastComponent != null) { - offset = groupOffset - 1; // lastComponent.getEndOffset(); - } - updateComponent(dataTypeComponent, nextOrdinal, offset, length, - alignment > 0 ? alignment : 1); + // special handling of zero-length bitfield when it is last component + int offset = DataOrganizationImpl.getAlignedOffset(zeroBitFieldDt.getBaseDataType().getAlignment(), groupOffset); + updateComponent(dataTypeComponent, nextOrdinal, offset, 0, alignment > 0 ? alignment : 1); groupOffset = -1; } else { @@ -261,7 +253,7 @@ class AlignedComponentPacker { groupOffset = zeroAlignmentOffset; } - updateComponent(lastComponent, ordinal, groupOffset, 1, minimumAlignment); + updateComponent(lastComponent, ordinal, groupOffset, 0, minimumAlignment); } private boolean packComponent(InternalDataTypeComponent dataTypeComponent) { @@ -306,8 +298,9 @@ class AlignedComponentPacker { private void alignAndPackNonBitfieldComponent(InternalDataTypeComponent dataTypeComponent, int minOffset) { DataType componentDt = dataTypeComponent.getDataType(); - int dtSize = componentDt.getLength(); - if (dtSize <= 0) { + + int dtSize = componentDt.isZeroLength() ? 0 : componentDt.getLength(); + if (dtSize < 0) { dtSize = dataTypeComponent.getLength(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java index 57842c80f5..2c00efbdcc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java @@ -26,8 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * AlignedStructureInspector provides a simple instance of a structure * member container used to perform alignment operations without forcing modification - * of the actual structure. A wrapper is not used for the flexible array component - * which will not be modified by packer. + * of the actual structure. */ public class AlignedStructureInspector extends AlignedStructurePacker { @@ -77,11 +76,6 @@ public class AlignedStructureInspector extends AlignedStructurePacker { return component.getParent(); } - @Override - public boolean isFlexibleArrayComponent() { - return component.isFlexibleArrayComponent(); - } - @Override public boolean isBitFieldComponent() { return component.isBitFieldComponent(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java index 6ca069481d..9e35c29368 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java @@ -100,16 +100,6 @@ public class AlignedStructurePacker { int length = packer.getLength(); componentsChanged |= packer.componentsChanged(); - DataTypeComponent flexibleArrayComponent = structure.getFlexibleArrayComponent(); - if (flexibleArrayComponent != null) { - // account for flexible array type and any end of structure padding required - int componentAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, - structure.getStoredPackingValue(), flexibleArrayComponent); - length = DataOrganizationImpl.getAlignedOffset(componentAlignment, length); - defaultAlignment = - DataOrganizationImpl.getLeastCommonMultiple(defaultAlignment, componentAlignment); - } - int alignment = defaultAlignment; AlignmentType alignmentType = structure.getAlignmentType(); if (alignmentType != AlignmentType.DEFAULT) { @@ -135,7 +125,7 @@ public class AlignedStructurePacker { * returned result. Component count is should only change if component * list includes DEFAULT members which will be ignored. * @param structure structure whose members are to be aligned/packed. - * @param components structure components (excludes any trailing flexible array). + * @param components structure components. * @return aligned packing result */ public static StructurePackResult packComponents(StructureInternal structure, diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Array.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Array.java index fc69ce8f6b..fe716695ec 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Array.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Array.java @@ -32,7 +32,10 @@ public interface Array extends DataType { int getNumElements(); /** - * Returns the length of an element in the array + * Returns the length of an element in the array. In the case + * of a Dynamic base datatype, this element length will have been explicitly specified + * at the time of construction. For a zero-length base type an element length of 1 + * will be reported with {@link #getLength()} returning the number of elements. * @return the length of one element in the array. */ int getElementLength(); @@ -99,6 +102,9 @@ public interface Array extends DataType { * @return a String if it is an array of chars; otherwise empty string, never null. */ default public String getArrayRepresentation(MemBuffer buf, Settings settings, int length) { + if (getNumElements() == 0) { + return ""; + } if (!buf.isInitializedMemory()) { return StringDataInstance.UNKNOWN; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java index 89713c07dd..cb2e6ef7ab 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java @@ -26,7 +26,6 @@ import ghidra.util.exception.DuplicateNameException; * Basic implementation of the Array interface. */ public class ArrayDataType extends DataTypeImpl implements Array { - private final static long serialVersionUID = 1; private int numElements; private DataType dataType; @@ -35,9 +34,11 @@ public class ArrayDataType extends DataTypeImpl implements Array { /** * Constructs a new Array dataType. - * @param dataType the dataType of the elements in the array. - * @param numElements the number of elements in the array. - * @param elementLength the length of an individual element in the array. + * @param dataType the dataType of the elements in the array (null is not permitted). + * @param numElements the number of elements in the array (0 is permitted). + * @param elementLength the length of an individual element in the array. This value + * is only used for {@link Dynamic} dataType where {@link Dynamic#canSpecifyLength()} + * returns true. */ public ArrayDataType(DataType dataType, int numElements, int elementLength) { this(dataType, numElements, elementLength, null); @@ -46,44 +47,63 @@ public class ArrayDataType extends DataTypeImpl implements Array { /** * Constructs a new Array dataType. * @param dataType the dataType of the elements in the array. - * @param numElements the number of elements in the array. - * @param elementLength the length of an individual element in the array. + * @param numElements the number of elements in the array (0 is permitted). + * @param elementLength the length of an individual element in the array. This value + * is only used for {@link Dynamic} dataType where {@link Dynamic#canSpecifyLength()} + * returns true. * @param dtm datatype manager or null */ public ArrayDataType(DataType dataType, int numElements, int elementLength, DataTypeManager dtm) { super(dataType.getCategoryPath(), "array", dtm); - validate(dataType); - if (dataType.getDataTypeManager() != dtm) { - dataType = dataType.clone(dtm); - } - int dtLen = dataType.getLength(); - if (dtLen < 0 && elementLength < 0) { - throw new IllegalArgumentException("Array DataType must be Fixed length"); - } - if (numElements <= 0) { + if (numElements < 0) { throw new IllegalArgumentException( - "number of array elements must be positive, not " + numElements); + "Number of array elements may not be negative [" + numElements + "]"); + } + DataType baseDt = dataType; + if (dataType instanceof TypeDef) { + baseDt = ((TypeDef) dataType).getBaseDataType(); + } + validate(baseDt); + dataType = dataType.clone(dtm); + this.elementLength = -1; + if (baseDt instanceof Dynamic) { + if (elementLength < 0) { + throw new IllegalArgumentException( + "Must specify Array element-length for dynamic " + + dataType.getClass().getSimpleName()); + } + this.elementLength = elementLength; } this.dataType = dataType; - this.elementLength = dtLen < 0 ? elementLength : -1; this.numElements = numElements; name = DataTypeUtilities.getName(this, true); dataType.addParent(this); } - private void validate(DataType dt) { - if (dt instanceof BitFieldDataType) { + /** + * Validate a array base datatype to ensure that it is allowed + * @param baseDt intended base datatype for array (always pass in typedef's base type if applicable) + * @throws IllegalArgumentException if dt is not a valid base datatype for an array + */ + private static void validate(DataType baseDt) throws IllegalArgumentException { + if (baseDt instanceof BitFieldDataType) { throw new IllegalArgumentException( - "Array data-type may not be a bitfield: " + dt.getName()); + "Array data-type may not be a bitfield: " + baseDt.getName()); } - if (dt instanceof FactoryDataType) { + if (baseDt instanceof FactoryDataType) { throw new IllegalArgumentException( - "Array data-type may not be a Factory data-type: " + dt.getName()); + "Array data-type may not be a Factory data-type: " + baseDt.getName()); } - if (dt instanceof Dynamic && !((Dynamic) dt).canSpecifyLength()) { + if (baseDt instanceof Dynamic) { + if (!((Dynamic) baseDt).canSpecifyLength()) { + throw new IllegalArgumentException( + "Array data-type may not be a non-sizable Dynamic data-type: " + baseDt.getName()); + } + } + else if (baseDt.getLength() < 1) { // not Dynamic throw new IllegalArgumentException( - "Array data-type may not be a non-sizable Dynamic data-type: " + dt.getName()); + "Data type may not report a length less than 1: " + baseDt.getClass().getSimpleName()); } } @@ -132,11 +152,14 @@ public class ArrayDataType extends DataTypeImpl implements Array { @Override public boolean isZeroLength() { - return dataType.isZeroLength(); + return numElements == 0; } @Override public int getLength() { + if (numElements == 0) { + return 1; // 0-length datatype instance not supported + } return numElements * getElementLength(); } @@ -189,7 +212,7 @@ public class ArrayDataType extends DataTypeImpl implements Array { @Override public int getElementLength() { - return elementLength < 0 ? dataType.getLength() : elementLength; + return (dataType instanceof Dynamic) ? elementLength : dataType.getLength(); } @Override @@ -207,21 +230,27 @@ public class ArrayDataType extends DataTypeImpl implements Array { @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { - if (newDt.getLength() < 0) { + if (newDt == this || newDt.getLength() < 0) { newDt = DataType.DEFAULT; } if (dataType == oldDt) { String oldName = getName(); + int oldLength = getLength(); + int oldAlignment = getAlignment(); int oldElementLength = getElementLength(); dataType.removeParent(this); dataType = newDt; dataType.addParent(this); elementLength = newDt.getLength() < 0 ? oldElementLength : -1; - notifyNameChanged(oldName); - - if (oldElementLength != getElementLength()) { + if (!getName().equals(oldName)) { + notifyNameChanged(oldName); + } + if (getLength() != oldLength || oldElementLength != getElementLength()) { notifySizeChanged(); } + else if (getAlignment() != oldAlignment) { + notifyAlignmentChanged(); + } } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java index 59e9091b7b..e51393f681 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java @@ -92,12 +92,9 @@ public class BitFieldDataType extends AbstractDataType { protected BitFieldDataType(DataType baseDataType, int bitSize) throws InvalidDataTypeException { this(baseDataType, bitSize, 0); } - - /** - * Determine if this bit-field has a zero length (i.e., alignment field) - * @return true if this bit-field has a zero length - */ - public boolean isZeroLengthField() { + + @Override + public boolean isZeroLength() { return bitSize == 0; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java index ae01c91e41..6b921c64be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java @@ -32,9 +32,7 @@ public interface Composite extends DataType { /** * Gets the number of component data types in this composite. * If this is Structure with packing disabled, the count will include all undefined filler - * components which may be present. In addition, Structures do not include the - * optional trailing flexible array component in this count - * (see {@link Structure#hasFlexibleArrayComponent()}). + * components which may be present. * @return the number of components that make up this composite */ public abstract int getNumComponents(); @@ -45,9 +43,6 @@ public interface Composite extends DataType { * since they do not contain undefined components. * This count will always exclude all undefined filler components which may be present * within a Structure whoose packing is disabled (see {@link #isPackingEnabled()}). - * In addition, Structures do not include the - * optional trailing flexible array component in this count - * (see {@link Structure#hasFlexibleArrayComponent()}). * @return the number of explicitly defined components in this composite */ public abstract int getNumDefinedComponents(); @@ -63,7 +58,6 @@ public interface Composite extends DataType { /** * Returns an array of Data Type Components that make up this composite including * undefined filler components which may be present within a Structure whch has packing disabled. - * Structures do not include the optional trailing flexible array component in the returned array. * The number of components corresponds to {@link #getNumComponents()}. * @return array all components */ @@ -74,9 +68,7 @@ public interface Composite extends DataType { * undefined filler components which may be present within Structures where packing is disabled. * The number of components corresponds to {@link #getNumDefinedComponents()}. For Unions and * packed Structures this is equivalent to {@link #getComponents()} - * since they do not contain undefined filler components. Structures do not include the - * optional trailing flexible array component in the returned array - * (see {@link Structure#getFlexibleArrayComponent()}). + * since they do not contain undefined filler components. * @return array all explicitly defined components */ public abstract DataTypeComponent[] getDefinedComponents(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java index beedd5cdfa..2c6e1b807f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java @@ -31,22 +31,6 @@ public class CompositeAlignmentHelper { impartedAlignment); } } - if (composite instanceof Structure) { - Structure struct = (Structure) composite; - DataTypeComponent flexibleArrayComponent = struct.getFlexibleArrayComponent(); - if (flexibleArrayComponent != null) { - allComponentsLCM = getComponentAlignmentLCM(dataOrganization, allComponentsLCM, - packingValue, flexibleArrayComponent); - } - } - return allComponentsLCM; - } - - private static int getComponentAlignmentLCM(DataOrganization dataOrganization, - int allComponentsLCM, int packingValue, DataTypeComponent component) { - int componentAlignment = getPackedAlignment(dataOrganization, packingValue, component); - allComponentsLCM = - DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM, componentAlignment); return allComponentsLCM; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java index 38f69201d6..2c971b8e82 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java @@ -96,6 +96,12 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C * @return preferred component length */ protected int getPreferredComponentLength(DataType dataType, int length) { + if (DataTypeComponent.usesZeroLengthComponent(dataType)) { + return 0; + } + if ((isPackingEnabled() || (this instanceof Union)) && !(dataType instanceof Dynamic)) { + length = -1; // force use of datatype size + } int dtLength = dataType.getLength(); if (length <= 0) { length = dtLength; @@ -113,6 +119,22 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public abstract boolean hasLanguageDependantLength(); + /** + * Determine if this composite should be treated as undefined. + *

+ * A composite is considered undefined with a zero-length when it has + * no components and packing is disabled. A {@link DataTypeComponent} defined by an + * an datatype which is not-yet-defined (i.e., {@link DataType#isNotYetDefined()} is true) + * will always have a size of 1. If an empty composite should be treated as + * fully specified, packing on the composite should be enabled to ensure that + * a zero-length component is used should the occassion arise (e.g., empty structure + * placed within union as a component). + */ + @Override + public final boolean isNotYetDefined() { + return getNumComponents() == 0 && !isPackingEnabled(); + } + @Override public boolean isPartOf(DataType dataTypeOfInterest) { return DataTypeUtilities.isSecondPartOfFirst(this, dataTypeOfInterest); @@ -407,123 +429,7 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C @Override public String toString() { - return toString(this); - } - - public static String toString(Composite composite) { - - StringBuilder stringBuffer = new StringBuilder(); - stringBuffer.append(composite.getPathName() + "\n"); - stringBuffer.append(getAlignmentAndPackingString(composite) + "\n"); - stringBuffer.append(getTypeName(composite) + " " + composite.getDisplayName() + " {\n"); - dumpComponents(composite, stringBuffer, " "); - stringBuffer.append("}\n"); - stringBuffer.append("Size = " + composite.getLength() + " Actual Alignment = " + - composite.getAlignment() + "\n"); - return stringBuffer.toString(); - - } - - /** - * Dump all components for use in {@link #toString()} representation. - * - * @param buffer string buffer - * @param pad padding to be used with each component output line - */ - private static void dumpComponents(Composite composite, StringBuilder buffer, String pad) { - // limit output of filler components for non-packed structures - DataTypeComponent[] components = composite.getDefinedComponents(); - for (DataTypeComponent dtc : components) { - DataType dataType = dtc.getDataType(); -// buffer.append(pad + dtc.getOrdinal()); -// buffer.append(") "); - buffer.append(pad + dtc.getOffset()); - buffer.append(pad + dataType.getName()); - if (dataType instanceof BitFieldDataType) { - BitFieldDataType bfDt = (BitFieldDataType) dataType; - buffer.append("("); - buffer.append(Integer.toString(bfDt.getBitOffset())); - buffer.append(")"); - } - buffer.append(pad + dtc.getLength()); - buffer.append(pad + dtc.getFieldName()); - String comment = dtc.getComment(); - if (comment == null) { - comment = ""; - } - buffer.append(pad + "\"" + comment + "\""); - buffer.append("\n"); - } - if (composite instanceof Structure) { - DataTypeComponent dtc = ((Structure) composite).getFlexibleArrayComponent(); - if (dtc != null) { - DataType dataType = dtc.getDataType(); - buffer.append(pad + dataType.getDisplayName() + "[0]"); - buffer.append(pad + dtc.getLength()); - buffer.append(pad + dtc.getFieldName()); - String comment = dtc.getComment(); - if (comment == null) { - comment = ""; - } - buffer.append(pad + "\"" + comment + "\""); - buffer.append("\n"); - } - } - } - - private static String getTypeName(Composite composite) { - if (composite instanceof Structure) { - return "Structure"; - } - else if (composite instanceof Union) { - return "Union"; - } - return ""; - } - - public static String getAlignmentAndPackingString(Composite composite) { - StringBuilder buf = - new StringBuilder(getMinAlignmentString(composite)); - if (buf.length() != 0) { - buf.append(" "); - } - buf.append(getPackingString(composite)); - return buf.toString(); - } - - public static String getMinAlignmentString(Composite composite) { - if (composite.isDefaultAligned()) { - return ""; - } - StringBuilder buf = new StringBuilder(ALIGN_NAME); - buf.append("("); - if (composite.isMachineAligned()) { - buf.append("machine:"); - buf.append(composite.getDataOrganization().getMachineAlignment()); - } - else { - buf.append(composite.getExplicitMinimumAlignment()); - } - buf.append(")"); - return buf.toString(); - } - - public static String getPackingString(Composite composite) { - StringBuilder buf = new StringBuilder(PACKING_NAME); - buf.append("("); - if (composite.isPackingEnabled()) { - if (composite.hasExplicitPackingValue()) { - buf.append(composite.getExplicitPackingValue()); - } - else { - buf.append(DEFAULT_PACKING_NAME); - } - } - else { - buf.append(DISABLED_PACKING_NAME); // NO_PACKING - } - buf.append(")"); - return buf.toString(); + return CompositeInternal.toString(this); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java index 7742102263..4d6f201bd1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java @@ -22,6 +22,12 @@ import java.util.Comparator; */ public interface CompositeInternal extends Composite { + // Strings used for toString formatting + static final String ALIGN_NAME = "aligned"; + static final String PACKING_NAME = "pack"; + static final String DISABLED_PACKING_NAME = "disabled"; + static final String DEFAULT_PACKING_NAME = ""; + /** * The stored packing value which corresponds to a composite that will automatically pack * based upon the alignment requirements of its components. A positive pack value will @@ -148,7 +154,109 @@ public interface CompositeInternal extends Composite { int ordinal = ((Integer) o2).intValue(); return dtc.getOrdinal() - ordinal; } + } + /** + * Dump composite and its components for use in {@link #toString()} representation. + * @param composite composite instance to be dumped + * @return formatted dump as string + */ + public static String toString(Composite composite) { + StringBuilder stringBuffer = new StringBuilder(); + stringBuffer.append(composite.getPathName() + "\n"); + stringBuffer.append(getAlignmentAndPackingString(composite) + "\n"); + stringBuffer.append(getTypeName(composite) + " " + composite.getDisplayName() + " {\n"); + dumpComponents(composite, stringBuffer, " "); + stringBuffer.append("}\n"); + stringBuffer.append("Size = " + composite.getLength() + " Actual Alignment = " + + composite.getAlignment() + "\n"); + return stringBuffer.toString(); + } + + /** + * Dump all components for use in {@link #toString()} representation. + * + * @param buffer string buffer + * @param pad padding to be used with each component output line + */ + private static void dumpComponents(Composite composite, StringBuilder buffer, String pad) { + // limit output of filler components for non-packed structures + DataTypeComponent[] components = composite.getDefinedComponents(); + for (DataTypeComponent dtc : components) { + DataType dataType = dtc.getDataType(); +// buffer.append(pad + dtc.getOrdinal()); +// buffer.append(") "); + buffer.append(pad + dtc.getOffset()); + buffer.append(pad + dataType.getName()); + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bfDt = (BitFieldDataType) dataType; + buffer.append("("); + buffer.append(Integer.toString(bfDt.getBitOffset())); + buffer.append(")"); + } + buffer.append(pad + dtc.getLength()); + buffer.append(pad + dtc.getFieldName()); + String comment = dtc.getComment(); + if (comment == null) { + comment = ""; + } + buffer.append(pad + "\"" + comment + "\""); + buffer.append("\n"); + } + } + + private static String getTypeName(Composite composite) { + if (composite instanceof Structure) { + return "Structure"; + } + else if (composite instanceof Union) { + return "Union"; + } + return ""; + } + + public static String getAlignmentAndPackingString(Composite composite) { + StringBuilder buf = new StringBuilder(getMinAlignmentString(composite)); + if (buf.length() != 0) { + buf.append(" "); + } + buf.append(getPackingString(composite)); + return buf.toString(); + } + + public static String getMinAlignmentString(Composite composite) { + if (composite.isDefaultAligned()) { + return ""; + } + StringBuilder buf = new StringBuilder(ALIGN_NAME); + buf.append("("); + if (composite.isMachineAligned()) { + buf.append("machine:"); + buf.append(composite.getDataOrganization().getMachineAlignment()); + } + else { + buf.append(composite.getExplicitMinimumAlignment()); + } + buf.append(")"); + return buf.toString(); + } + + public static String getPackingString(Composite composite) { + StringBuilder buf = new StringBuilder(PACKING_NAME); + buf.append("("); + if (composite.isPackingEnabled()) { + if (composite.hasExplicitPackingValue()) { + buf.append(composite.getExplicitPackingValue()); + } + else { + buf.append(DEFAULT_PACKING_NAME); + } + } + else { + buf.append(DISABLED_PACKING_NAME); // NO_PACKING + } + buf.append(")"); + return buf.toString(); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java index 995c8d6b75..d8f49d8102 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java @@ -29,8 +29,22 @@ import ghidra.util.exception.DuplicateNameException; */ public interface DataType { - public static final DataType DEFAULT = DefaultDataType.dataType; // global default data type - public static final DataType VOID = VoidDataType.dataType; // global void data type + /** + * WARNING: do not add default method implementations to this interface. + * Doing so intereferes with correct initialization of the static instance variables + * {@link #DEFAULT} and {@link #VOID} below. + */ + + /** + * Singleton instance of default datatype. + */ + public static final DataType DEFAULT = DefaultDataType.dataType; + + /** + * Instance of void datatype (never use ==) + * @deprecated should use {@link VoidDataType#dataType} instead + */ + public static final DataType VOID = VoidDataType.dataType; public final static String CONFLICT_SUFFIX = ".conflict"; @@ -44,14 +58,6 @@ public interface DataType { */ public boolean hasLanguageDependantLength(); - /** - * Indicates if type has not yet been defined. - * Such types will always return a size of 1. - * (example: empty structure) - * @return true if this type is not yet defined. - */ - public boolean isNotYetDefined(); - /** * Gets a list of all the settingsDefinitions used by this data type. * @return a list of the settingsDefinitions used by this data type. @@ -157,22 +163,38 @@ public interface DataType { public String getMnemonic(Settings settings); /** - * Get the length (number of 8-bit bytes) of this DataType. + * Get the length (number of 8-bit bytes) of this DataType. + *

P + * NOTE: No datatype should ever return 0, even if {@link #isZeroLength()}, and only + * {@link Dynamic} datatypes should return -1. If {@link #isZeroLength()} is true a length of 1 + * should be returned. Where a zero-length datatype can be handled (e.g., {@link Composite}) + * the {@link #isZeroLength()} method should be used. * @return the length of this DataType */ public int getLength(); /** - * Indicates is this datatype is defined with a zero length. + * Indicates this datatype is defined with a zero length. * This method should not be confused with {@link #isNotYetDefined()} * which indicates that nothing but the name and basic type is known. - * NOTE: Support for zero-length datatypes is not yet fully supported, as a result - * they will generally return a non-zero length. - * @return true if type definition has a length of 0 even though it may report - * a length of 1, else false. + * NOTE: a zero-length datatype must return a length of 1 + * via {@link #getLength()}. Zero-length datatypes used as a + * component within a {@link Composite} may, or may not, be assigned + * a component length of 0. The method {@link DataTypeComponent#usesZeroLengthComponent(DataType)} + * is used to make this determination. + * @return true if type definition has a length of 0, else false */ public boolean isZeroLength(); + /** + * Indicates if this datatype has not yet been fully defined. + * Such datatypes should always return a {@link #getLength()} of 1 and + * true for {@link #isZeroLength()}. + * (example: empty structure) + * @return true if this type is not yet defined. + */ + public boolean isNotYetDefined(); + /** * Get a String briefly describing this DataType. * @@ -450,6 +472,7 @@ public interface DataType { /** * Returns the DataOrganization associated with this data-type + * @return associated data organization */ public DataOrganization getDataOrganization(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java index 50bb8a934e..9302356b6d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponent.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.model.data; import ghidra.docking.settings.Settings; @@ -24,10 +21,6 @@ import ghidra.util.exception.DuplicateNameException; /** * DataTypeComponents are holders for the dataTypes that make up composite (Structures * and Unions) dataTypes. - *

- * While most all components must have a fixed length greater than 0, structures support an - * optional trailing flexible array component whose length is zero and whose offset equals - * the length of the structure. */ public interface DataTypeComponent { @@ -36,9 +29,6 @@ public interface DataTypeComponent { /** * Returns the dataType in this component. - *

- * NOTE: If this component corresponds to a structure flexible array the returned data type - * reflects the base type of the array (e.g., char is returned for a flexible char array). * @return the dataType in this component */ public DataType getDataType(); @@ -49,13 +39,6 @@ public interface DataTypeComponent { */ public DataType getParent(); - /** - * Determine if this component corresponds to a unsized flexible array which is - * permitted as the trailing component within a structure. - * @return true if component is a trailing flexible array component. - */ - public boolean isFlexibleArrayComponent(); - /** * Determine if the specified component corresponds to a bit-field. * @return true if bit-field else false @@ -64,16 +47,12 @@ public interface DataTypeComponent { /** * Determine if the specified component corresponds to a zero-length bit-field. - * @return true if zer-length bit-field else false + * @return true if zero-length bit-field else false */ public boolean isZeroBitFieldComponent(); /** * Get the ordinal position within the parent dataType. - *

- * NOTE: The special case of a structure flexible array component returns an ordinal equal - * to the parent structure's {@link Structure#getNumComponents()} since it is not included - * in the list of normal components (see {@link Structure#getFlexibleArrayComponent()}. * @return ordinal of this component within the parent data type. */ public int getOrdinal(); @@ -81,10 +60,6 @@ public interface DataTypeComponent { /** * Get the byte offset of where this component begins relative to the start of the parent * data type. - *

- * NOTE: The special case of a structure flexible array component returns an offset equal - * to the length of the parent structure since the flexible array component is not included - * in a structure's length. * @return offset of start of component relative to the start of the parent * data type. */ @@ -93,25 +68,22 @@ public interface DataTypeComponent { /** * Get the byte offset of where this component ends relative to the start of the parent * data type. - *

- * NOTE: The special case of a structure flexible array component returns -1 since its - * length is undefined. * @return offset of end of component relative to the start of the parent * data type. */ public int getEndOffset(); /** - * Get the length of this component. - *

- * NOTE: The special case of a structure flexible array component returns 0 since its - * length is undefined. - * @return the length of this component or 0 for a structure flexible array. + * Get the length of this component. Zero-length components will report a length of 0 + * and may overlap other components at the same offset. Similarly, multiple adjacent + * bit-field components may appear to overlap at the byte-level. + * @return the length of this component */ public int getLength(); /** * Get the comment for this dataTypeComponent. + * @return component comment string or null if one has not been set */ public String getComment(); @@ -129,7 +101,7 @@ public interface DataTypeComponent { /** * Sets the comment for the component. - * @param comment this components comment. + * @param comment this components comment or null to clear comment. */ public void setComment(String comment); @@ -153,8 +125,18 @@ public interface DataTypeComponent { /** * Returns a default Field name. Used only if a field name is not set. + * @return default field name */ - public String getDefaultFieldName(); + public default String getDefaultFieldName() { + if (isZeroBitFieldComponent()) { + return ""; + } + String name = DEFAULT_FIELD_NAME_PREFIX + getOrdinal(); + if (getParent() instanceof Structure) { + name += "_0x" + Integer.toHexString(getOffset()); + } + return name; + } /** * Returns true if the given dataTypeComponent is equivalent to this dataTypeComponent. @@ -168,4 +150,28 @@ public interface DataTypeComponent { */ public boolean isEquivalent(DataTypeComponent dtc); + /** + * Determine if the specified dataType will be treated as a zero-length component + * allowing it to possibly overlap the next component. If the specified dataType + * returns true for {@link DataType#isZeroLength()} and true for {@link DataType#isNotYetDefined()} + * this method will return false causing the associated component to use the reported dataType length + * of 1. + * @param dataType datatype to be evaluated + * @return true if zero-length component + */ + public static boolean usesZeroLengthComponent(DataType dataType) { + if (dataType.isZeroLength()) { + if (dataType instanceof TypeDef) { + // need to check base type since TypeDef always returns false for isNotYetDefined() + dataType = ((TypeDef) dataType).getBaseDataType(); + } + if (dataType instanceof Array) { + return true; + } + // assumes undefined types will ultimately have a non-zero length + return !dataType.isNotYetDefined(); + } + return false; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java index 6dcbd43876..456723fb78 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java @@ -38,7 +38,6 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali private String fieldName; // name of this prototype in the component private String comment; // comment about this component. private int length; // my length - private boolean isFlexibleArrayComponent = false; /** * Create a new DataTypeComponent @@ -60,16 +59,6 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali this.fieldName = fieldName; this.comment = comment; setDataType(dataType); - initFlexibleArrayComponent(); - } - - private void initFlexibleArrayComponent() { - if (dataType instanceof BitFieldDataType || dataType instanceof Dynamic || - dataType instanceof FactoryDataType) { - return; - } - isFlexibleArrayComponent = - length == 0 && offset < 0 && ordinal < 0 && (parent instanceof Structure); } /** @@ -85,11 +74,6 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali this(dataType, parent, length, ordinal, offset, null, null); } - @Override - public boolean isFlexibleArrayComponent() { - return isFlexibleArrayComponent; - } - @Override public boolean isBitFieldComponent() { return dataType instanceof BitFieldDataType; @@ -106,25 +90,21 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali @Override public int getOffset() { - if (isFlexibleArrayComponent) { - if (parent.isZeroLength()) { - // some structures have only a flexible array defined - return 0; - } - return parent.getLength(); - } return offset; } boolean containsOffset(int off) { - if (isFlexibleArrayComponent) { - return false; + if (off == offset) { // separate check required to handle zero-length case + return true; } - return off >= offset && off <= (offset + length - 1); + return off > offset && off < (offset + length); } @Override public int getEndOffset() { + if (length == 0) { // separate check required to handle zero-length case + return offset; + } return offset + length - 1; } @@ -146,17 +126,6 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali return fieldName; } - @Override - public String getDefaultFieldName() { - if (isZeroBitFieldComponent()) { - return ""; - } - if (parent instanceof Structure) { - return DEFAULT_FIELD_NAME_PREFIX + "_0x" + Integer.toHexString(getOffset()); - } - return DEFAULT_FIELD_NAME_PREFIX + getOrdinal(); - } - @Override public void setFieldName(String name) throws DuplicateNameException { if (name != null) { @@ -244,9 +213,6 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali @Override public int getOrdinal() { - if (isFlexibleArrayComponent) { - return parent.getNumComponents(); - } return ordinal; } @@ -287,10 +253,8 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali DataType myDt = getDataType(); DataType otherDt = dtc.getDataType(); - // NOTE: use getOffset() and getOrdinal() methods since returned values will differ from - // stored values for flexible array component - if (getOffset() != dtc.getOffset() || getLength() != dtc.getLength() || - getOrdinal() != dtc.getOrdinal() || + if (offset != dtc.getOffset() || getLength() != dtc.getLength() || + ordinal != dtc.getOrdinal() || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; @@ -330,9 +294,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali boolean aligned = (myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false; // Components don't need to have matching offset when they are aligned - // NOTE: use getOffset() method since returned values will differ from - // stored values for flexible array component - if ((!aligned && (getOffset() != dtc.getOffset())) || + if ((!aligned && (offset != dtc.getOffset())) || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; @@ -348,29 +310,21 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali @Override public void setDataType(DataType dt) { + // intended for internal use only dataType = dt; - if (dt instanceof BitFieldDataType) { - // bit-field packing may change component size - setLength(dt.getLength()); - } + } + + /** + * Determine if component is an undefined filler component + * @return true if undefined filler component, else false + */ + boolean isUndefined() { + return dataType == DataType.DEFAULT; } @Override public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(" " + ordinal); - buffer.append(" " + offset); - buffer.append(" " + dataType.getName()); - if (isFlexibleArrayComponent) { - buffer.append("[ ]"); - } - else if (dataType instanceof BitFieldDataType) { - buffer.append("(" + ((BitFieldDataType) dataType).getBitOffset() + ")"); - } - buffer.append(" " + length); - buffer.append(" " + fieldName); - buffer.append(" " + ((comment != null) ? ("\"" + comment + "\"") : comment)); - return buffer.toString(); + return InternalDataTypeComponent.toString(this); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java index 42088a817e..385885b56b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeWriter.java @@ -447,15 +447,7 @@ public class DataTypeWriter { deferWrite(componentType); // TODO the return value of this is not used--delete? - getTypeDeclaration(null, componentType, component.getLength(), false, true, monitor); - } - - if (composite instanceof Structure) { - Structure s = (Structure) composite; - if (s.hasFlexibleArrayComponent()) { - DataType componentType = s.getFlexibleArrayComponent().getDataType(); - deferWrite(componentType); - } + getTypeDeclaration(null, componentType, component.getLength(), true, monitor); } } @@ -478,13 +470,6 @@ public class DataTypeWriter { writeComponent(component, composite, sb, monitor); } - if (composite instanceof Structure) { - Structure s = (Structure) composite; - if (s.hasFlexibleArrayComponent()) { - writeComponent(s.getFlexibleArrayComponent(), composite, sb, monitor); - } - } - sb.append(annotator.getSuffix(composite, null)); sb.append("};"); @@ -505,8 +490,8 @@ public class DataTypeWriter { DataType componentDataType = component.getDataType(); - sb.append(getTypeDeclaration(fieldName, componentDataType, component.getLength(), - component.isFlexibleArrayComponent(), false, monitor)); + sb.append(getTypeDeclaration(fieldName, componentDataType, component.getLength(), false, + monitor)); sb.append(";"); sb.append(annotator.getSuffix(composite, component)); @@ -519,7 +504,7 @@ public class DataTypeWriter { } private String getTypeDeclaration(String name, DataType dataType, int instanceLength, - boolean isFlexArray, boolean writeEnabled, TaskMonitor monitor) + boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException { if (name == null) { @@ -560,9 +545,6 @@ public class DataTypeWriter { } else { componentString = getDataTypePrefix(dataType) + dataType.getDisplayName(); - if (isFlexArray) { - componentString += "[0]"; - } if (name.length() != 0) { componentString += " " + name; } @@ -665,7 +647,7 @@ public class DataTypeWriter { writeDeferredDeclarations(monitor); } - String typedefString = getTypeDeclaration(typedefName, dataType, -1, false, true, monitor); + String typedefString = getTypeDeclaration(typedefName, dataType, -1, true, monitor); writer.write("typedef " + typedefString + ";"); writer.write(EOL); @@ -877,8 +859,8 @@ public class DataTypeWriter { if (writeEnabled) { write(dataType, monitor); } - String argument = getTypeDeclaration(paramName, dataType, param.getLength(), false, - writeEnabled, monitor); + String argument = + getTypeDeclaration(paramName, dataType, param.getLength(), writeEnabled, monitor); buf.append(argument); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java index e7bf8bb3f3..bc1d774fec 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DefaultDataType.java @@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException; * particular type of data in the program. */ public class DefaultDataType extends DataTypeImpl { - private final static long serialVersionUID = 1; /** A statically defined DefaultDataType used when an Undefined byte is needed.*/ public static DefaultDataType dataType = new DefaultDataType(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Dynamic.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Dynamic.java index ec77899b37..e566b6460f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Dynamic.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Dynamic.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,7 @@ */ package ghidra.program.model.data; +import ghidra.program.model.listing.Data; import ghidra.program.model.mem.MemBuffer; /** @@ -39,9 +39,13 @@ public interface Dynamic extends BuiltInDataType { int getLength(MemBuffer buf, int maxLength); /** - * Returns true if a user-specified length can be used + * Determine if the length may be specified for an instanceof this + * datatype (e.g., {@link Data}, {@link Array}, {@link DataTypeComponent}, etc.). + * @return true if a user-specified length can be used, else false */ - boolean canSpecifyLength(); + default boolean canSpecifyLength() { + return false; + } /** * Returns a suitable replacement base data-type for pointers and arrays 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 a5ff438a43..b56df48fc1 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 @@ -18,7 +18,6 @@ package ghidra.program.model.data; import ghidra.docking.settings.SettingsImpl; import ghidra.program.model.address.Address; import ghidra.program.model.mem.MemBuffer; -import ghidra.util.Msg; import ghidra.util.datastruct.SoftCacheMap; /** @@ -65,9 +64,7 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { if (comps == null || comps.length == 0) { return -1; } - DataTypeComponent last = comps[comps.length - 1]; - return (last != null && last.isFlexibleArrayComponent()) ? (comps.length - 1) - : comps.length; + return comps.length; } protected DataTypeComponent[] getComps(MemBuffer buf) { @@ -79,15 +76,6 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { // data-type not valid at buf location return null; } - for (int i = 0; i < comps.length - 1; i++) { - // TODO: should we verify ordinals - if (comps[i] != null && comps[i].isFlexibleArrayComponent()) { - // Only the last component may be a flexible array - Msg.error(this, - getClass().getName() + " produced invalid flexible array component"); - return null; - } - } map.put(addr, comps); } return comps; @@ -105,28 +93,27 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { public final DataTypeComponent getComponent(int ordinal, MemBuffer buf) { DataTypeComponent[] comps = getComps(buf); if (comps != null) { - DataTypeComponent dtc = comps[ordinal]; - if (dtc != null && !dtc.isFlexibleArrayComponent()) { - return dtc; - } + return comps[ordinal]; } return null; } /** * Returns an array of components that make up this data type. - * Could return null if there are no subcomponents. The last component - * in the array may be a flexible array component. + * Could return null if there are no subcomponents. * @param buf a memory buffer to be used by dataTypes that change depending on * their data context. - * @return datatype component array or null. Last component may be a flexible array component. + * @return datatype component array or null. */ public final DataTypeComponent[] getComponents(MemBuffer buf) { return getComps(buf); } /** - * Returns the first component containing the byte at the given offset + * Returns the first component containing the byte at the given offset. + * It is possible with zero-length components (see {@link DataTypeComponent#isZeroLength()}) + * and bitfields (see @DataTypeComponent#isBitFieldComponent()} for multiple components + * 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 @@ -137,16 +124,13 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { if (comps == null) { return null; } - // TODO: could use binary search (watchout for bitfields at same offset) + // TODO: could use binary search similar to StructureDataType for (DataTypeComponent comp : comps) { if (comp == null) { continue; } - if (comp.isFlexibleArrayComponent()) { - return null; - } if (offset >= comp.getOffset() && - (offset <= (comp.getOffset() + comp.getLength() - 1))) { + offset <= comp.getEndOffset()) { return comp; } } @@ -157,7 +141,7 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { * Get all dynamic components associated with the specified MemBuffer * @param buf memory buffer positioned at start of data type instance * @return all components or null if memory data is not valid for this - * data type. Last component may be a flexible array component. + * data type. */ protected abstract DataTypeComponent[] getAllComponents(MemBuffer buf); @@ -168,10 +152,11 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { return -1; } DataTypeComponent last = comps[comps.length - 1]; - if (last != null && last.isFlexibleArrayComponent()) { - return last.getOffset(); - } - return last.getOffset() + last.getLength(); + int lastComponentLength = last.getLength(); + return last.getOffset() + lastComponentLength; + // NOTE: any trailing alignment padding must be represented with + // undefined components to achieve the correct length. It may be + // best to do this for all padding within the component structure. } @Override @@ -188,19 +173,4 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic { return ByteDataType.dataType; } - /** - * Get trailing flexible array component. - * @return flexible array component or null if not applicable or valie. - */ - public final DataTypeComponent getFlexibleArrayComponent(MemBuffer buf) { - DataTypeComponent[] comps = getComps(buf); - if (comps == null || comps.length == 0) { - return null; - } - DataTypeComponent last = comps[comps.length - 1]; - if (last != null && last.isFlexibleArrayComponent()) { - return last; - } - return null; - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java index f735d8b10f..6a1fed94d9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java @@ -25,7 +25,7 @@ import ghidra.framework.store.db.PackedDatabase; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; /** * DataTypeManager for a file. Can import categories from a file, or export @@ -47,7 +47,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager private PackedDatabase packedDB; /** - * Construct a new DataTypeFileManager + * Construct a new DataTypeFileManager using the default data organization. * @param packedDbfile file to load or create based upon openMode * @param openMode one of the DBConstants: CREATE, UPDATE, READ_ONLY, UPGRADE * @throws IOException @@ -67,7 +67,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager } /** - * Create a new data-type file archive + * Create a new data-type file archive using the default data organization * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @return data-type manager backed by specified packedDbFile * @throws IOException @@ -77,7 +77,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager } /** - * Open an existing data-type file archive + * Open an existing data-type file archive using the default data organization * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @param openForUpdate if true archive will be open for update * @return data-type manager backed by specified packedDbFile @@ -89,7 +89,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager } /** - * Open an existing data-type file archive + * Open an existing data-type file archive using the default data organization * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @param openForUpdate if true archive will be open for update * @return data-type manager backed by specified packedDbFile @@ -107,6 +107,8 @@ public class FileDataTypeManager extends StandAloneDataTypeManager * match another existing archive database. * @param saveFile the file to save * @param newUniversalId the new id to use + * @throws DuplicateFileException + * @throws IOException */ public void saveAs(File saveFile, UniversalID newUniversalId) throws DuplicateFileException, IOException { @@ -116,7 +118,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager try { universalID = newUniversalId; packedDB = ((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(), - saveFile.getName(), newUniversalId.getValue(), TaskMonitorAdapter.DUMMY_MONITOR); + saveFile.getName(), newUniversalId.getValue(), TaskMonitor.DUMMY); file = resourceSaveFile; updateRootCategoryName(resourceSaveFile, getRootCategory()); } @@ -134,7 +136,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager validateFilename(resourceSaveFile); try { packedDB = ((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(), - saveFile.getName(), TaskMonitorAdapter.DUMMY_MONITOR); + saveFile.getName(), TaskMonitor.DUMMY); file = resourceSaveFile; updateRootCategoryName(resourceSaveFile, getRootCategory()); } @@ -153,7 +155,7 @@ public class FileDataTypeManager extends StandAloneDataTypeManager } try { - ((PackedDBHandle) dbHandle).save(TaskMonitorAdapter.DUMMY_MONITOR); + ((PackedDBHandle) dbHandle).save(TaskMonitor.DUMMY); } catch (CancelledException e) { // Cancel can't happen because we are using a dummy monitor diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java index 32175bd83d..540fd06305 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/InternalDataTypeComponent.java @@ -18,7 +18,8 @@ package ghidra.program.model.data; public interface InternalDataTypeComponent extends DataTypeComponent { /** - * Sets the DataType for this component + * Sets the DataType for this component. Must be used carefully since the component + * will not be resized. * @param dataType the new DataType for this component */ public void setDataType(DataType dataType); @@ -31,4 +32,19 @@ public interface InternalDataTypeComponent extends DataTypeComponent { */ void update(int ordinal, int offset, int length); + public static String toString(DataTypeComponent c) { + StringBuffer buffer = new StringBuffer(); + buffer.append(" " + c.getOrdinal()); + buffer.append(" " + c.getOffset()); + buffer.append(" " + c.getDataType().getName()); + if (c.isBitFieldComponent()) { + buffer.append("(" + ((BitFieldDataType) c.getDataType()).getBitOffset() + ")"); + } + buffer.append(" " + c.getLength()); + buffer.append(" " + c.getFieldName()); + String cmt = c.getComment(); + buffer.append(" " + ((cmt != null) ? ("\"" + cmt + "\"") : cmt)); + return buffer.toString(); + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ParameterDefinitionImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ParameterDefinitionImpl.java index 25384ef8ca..3667d254bb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ParameterDefinitionImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ParameterDefinitionImpl.java @@ -16,9 +16,9 @@ package ghidra.program.model.data; import ghidra.program.database.data.DataTypeUtilities; -import ghidra.program.model.listing.Parameter; -import ghidra.program.model.listing.Variable; +import ghidra.program.model.listing.*; import ghidra.program.model.symbol.SymbolUtilities; +import ghidra.util.exception.InvalidInputException; public class ParameterDefinitionImpl implements ParameterDefinition { @@ -56,55 +56,28 @@ public class ParameterDefinitionImpl implements ParameterDefinition { } /** - * Validate the specified datatype based upon its use as a parameter or return type. - * Ensure that the datatype has been cloned to the specified datatype manager (dtMgr). - * @param dataType datatype to be validated - * @param dtMgr target datatype manager - * @param isReturn true if checking return datatype, false if parameter datatype. - * @return datatype suitable for use within the target {@link FunctionDefinition}. - * @throws IllegalArgumentException if invalid datatype specified + * Check the specified datatype for use as a return, parameter or variable type. It may + * not be suitable for other uses. The following datatypes will be mutated into a default pointer datatype: + *

    + *
  • Function definition datatype
  • + *
  • An unsized/zero-element array
  • + *
+ * @param dataType datatype to be checked. If null specified the DEFAULT datatype will be returned. + * @param dtMgr target datatype manager (null permitted which will adopt default data organization) + * @param voidOK true if checking return datatype and void is allow, else false. + * @return cloned/mutated datatype suitable for function parameters and variables (including function return data type). + * @throws IllegalArgumentException if an unacceptable datatype was specified */ public static DataType validateDataType(DataType dataType, DataTypeManager dtMgr, - boolean isReturn) throws IllegalArgumentException { - String kind = isReturn ? "Return" : "Parameter"; - if (dataType == null) { - dataType = DataType.DEFAULT; + boolean voidOK) throws IllegalArgumentException { + try { + return VariableUtilities.checkDataType(dataType, voidOK, dtMgr); } - else if (dataType instanceof FunctionDefinition || (dataType instanceof TypeDef && - ((TypeDef) dataType).getBaseDataType() instanceof FunctionDefinition)) { - dataType = new PointerDataType(dataType, dtMgr); + catch (InvalidInputException e) { + throw new IllegalArgumentException(e.getMessage()); } - else if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) { - throw new IllegalArgumentException( - kind + " type may not be defined with Dynamic or Factory data-type: " + - dataType.getName()); - } - dataType = dataType.clone(dtMgr != null ? dtMgr : dataType.getDataTypeManager()); - if (dataType.getLength() < 0) { - throw new IllegalArgumentException(kind + - " type must be specified with fixed-length data type: " + dataType.getName()); - } - DataType baseType = dataType; - if(baseType instanceof TypedefDataType) { - baseType = ((TypedefDataType)baseType).getBaseDataType(); - } - - if (baseType instanceof VoidDataType) { - if (!isReturn) { - throw new IllegalArgumentException( - "Parameter type may not specify the void datatype - empty parameter list should be used"); - } - } - else if (dataType.getLength() == 0) { - throw new IllegalArgumentException(kind + - " type must be specified with fixed-length data type: " + dataType.getName()); - } - return dataType; } - /** - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public final int compareTo(ParameterDefinition p) { return ordinal - p.getOrdinal(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Pointer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Pointer.java index 71d4adcd2f..7fd9e5e281 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Pointer.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Pointer.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.model.data; /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java index 215a1e2d30..d239ac0ae6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java @@ -26,17 +26,16 @@ import ghidra.util.exception.DuplicateNameException; * DataTypeComponents from dataTypes that can not be modified. */ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializable { - private final static long serialVersionUID = 1; - - private DataType dataType; - private DynamicDataType parent; // parent prototype containing us - private int offset; // offset in parent - private int ordinal; // position in parent - private Settings settings; + private final DataType dataType; + private final DynamicDataType parent; // parent prototype containing us + private final int offset; // offset in parent + private final int ordinal; // position in parent + private final String comment; // comment about this component. + private final int length; // my length + private String fieldName; // name of this prototype in the component - private String comment; // comment about this component. - private int length; // my length + private Settings settings; /** * Create a new DataTypeComponent @@ -73,11 +72,6 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl this(dataType, parent, length, ordinal, offset, null, null); } - @Override - public boolean isFlexibleArrayComponent() { - return false; // Unsupported use - } - @Override public boolean isBitFieldComponent() { return dataType instanceof BitFieldDataType; @@ -91,40 +85,26 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl return false; } - /** - * @see ghidra.program.model.data.DataTypeComponent#getOffset() - */ @Override public int getOffset() { return offset; } - /** - * @see ghidra.program.model.data.DataTypeComponent#getEndOffset() - */ @Override public int getEndOffset() { return offset + length - 1; } - /** - * @see ghidra.program.model.data.DataTypeComponent#getComment() - */ @Override public String getComment() { return comment; } - /** - * @see ghidra.program.model.data.DataTypeComponent#setComment(java.lang.String) - */ @Override public void setComment(String comment) { + // ignore - read-only } - /** - * @see ghidra.program.model.data.DataTypeComponent#getFieldName() - */ @Override public String getFieldName() { if (fieldName == null) { @@ -133,76 +113,39 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl return fieldName; } - /** - * @see ghidra.program.model.data.DataTypeComponent#getDefaultFieldName() - */ @Override public String getDefaultFieldName() { return "field_" + getOrdinal(); } - /** - * @see ghidra.program.model.data.DataTypeComponent#setFieldName(java.lang.String) - */ @Override public void setFieldName(String fieldName) throws DuplicateNameException { + // ignore - read-only } - /** - * @see ghidra.program.model.data.DataTypeComponent#getDataType() - */ @Override public DataType getDataType() { return dataType; } - /** - * @see ghidra.program.model.data.DataTypeComponent#getParent() - */ @Override public DataType getParent() { return parent; } - /** - * Set the byte offset of where this component begins in its immediate parent - * data type. - * @param offset the offset - */ - void setOffset(int offset) { - this.offset = offset; - } - - /** - * @see ghidra.program.model.data.DataTypeComponent#getLength() - */ @Override public int getLength() { + if (length == 0) { + return 1; + } return length; } - void setLength(int length) { - this.length = length; - } - - /** - * @see ghidra.program.model.data.DataTypeComponent#getOrdinal() - */ @Override public int getOrdinal() { return ordinal; } - /** - * @param ordinal - */ - void setOrdinal(int ordinal) { - this.ordinal = ordinal; - } - - /** - * @see ghidra.program.model.data.DataTypeComponent#getDefaultSettings() - */ @Override public Settings getDefaultSettings() { if (settings == null) { @@ -211,17 +154,11 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl return settings; } - /** - * @see ghidra.program.model.data.DataTypeComponent#setDefaultSettings(ghidra.docking.settings.Settings) - */ @Override public void setDefaultSettings(Settings settings) { this.settings = settings; } - /** - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (!(obj instanceof DataTypeComponent)) { @@ -238,9 +175,6 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl isSameString(comment, dtc.getComment()); } - /* (non-Javadoc) - * @see ghidra.program.model.data.DataTypeComponent#isEquivalent(ghidra.program.model.data.DataTypeComponent) - */ @Override public boolean isEquivalent(DataTypeComponent dtc) { DataType myDt = getDataType(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java index 8ee2d330aa..1095fd5c59 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java @@ -34,11 +34,23 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { private Long transaction; /** - * Default constructor for temporary data-type manager. + * Constructor for new temporary data-type manager using the default DataOrganization. + * Note that this manager does not support the save or saveAs operation. * @param rootName Name of the root category. */ public StandAloneDataTypeManager(String rootName) { - super(); + super(DataOrganizationImpl.getDefaultOrganization()); + this.name = rootName; + } + + /** + * Constructor for new temporary data-type manager using a specified DataOrganization. + * Note that this manager does not support the save or saveAs operation. + * @param rootName Name of the root category. + * @param dataOrganzation applicable data organization + */ + public StandAloneDataTypeManager(String rootName, DataOrganization dataOrganzation) { + super(dataOrganzation); this.name = rootName; } @@ -70,10 +82,15 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT); } + @Override + public final DataOrganization getDataOrganization() { + return super.getDataOrganization(); + } + @Override public synchronized int startTransaction(String description) { if (transaction == null) { - transaction = new Long(dbHandle.startTransaction()); + transaction = dbHandle.startTransaction(); } transactionCount++; return transaction.intValue(); 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 e470429cf4..0f2a92f11f 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 @@ -16,11 +16,12 @@ package ghidra.program.model.data; import java.util.Comparator; +import java.util.List; /** * The structure interface. *

- * NOTE: Structures containing only a flexible array will report a length of 1 which will result in + * NOTE: A zero-length Structure will report a length of 1 which will result in * improper code unit sizing since we are unable to support a defined data of length 0. *

* NOTE: The use of zero-length bitfields within non-packed structures is discouraged since they have @@ -33,39 +34,72 @@ public interface Structure extends Composite { public Structure clone(DataTypeManager dtm); /** - * Returns the component of this structure with the indicated ordinal. If the specified ordinal - * equals {@link #getNumComponents()} the defined flexible array component will be returned, - * otherwise an out of bounds exception will be thrown. Use of - * {@link #getFlexibleArrayComponent()} is preferred for obtaining this special trailing - * component. + * Returns the component of this structure with the indicated ordinal. * * @param ordinal the ordinal of the component requested. * @return the data type component. * @throws IndexOutOfBoundsException if the ordinal is out of bounds */ @Override - public abstract DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException; + public DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException; /** - * Gets the immediate child component that contains the byte at the given offset. If the - * specified offset corresponds to a bit-field,the first bit-field component containing the - * offset will be returned. + * Gets the first immediate child component located at or after the given offset. * - * @param offset the byte offset into this data type - * @return the immediate child 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. */ - public abstract DataTypeComponent getComponentAt(int offset); + public DataTypeComponent getDefinedComponentAtOrAfterOffset(int offset); /** - * Returns the primitive Data Type that is at this offset. This is useful for prototypes that - * have components that are made up of other components If the specified offset corresponds to a - * bit-field,the BitFieldDataType of the first bit-field component containing the offset will be - * returned. + * Gets the first immediate child component that contains the byte at the given 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. + * + * @param offset the byte offset into this structure + * @return the first immediate child component containing 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. + * 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. + * + * @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. + */ + public default DataTypeComponent getComponentAt(int offset) { + return getComponentContaining(offset); + } + + /** + * 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. + * + * @param offset the byte offset into this structure + * @return a list of zero or more child components containing the specified offset + */ + public List getComponentsContaining(int offset); + + /** + * Returns the lowest-level component that contains the specified offset. This is useful + * for structures that have sub-structures. This method is best used when working with + * known structures which do not contain bitfields or zero-length components since in + * those situations multiple components may correspond to the specified offset. + * A similar ambiguous condition occurs if offset corresponds to a union component. * * @param offset the byte offset into this data type. - * @return the primitive data type at the offset. + * @return a primitive component data type which contains the specified offset. */ - public abstract DataTypeComponent getDataTypeAt(int offset); + public DataTypeComponent getDataTypeAt(int offset); /** * Inserts a new bitfield at the specified ordinal position in this structure. Within packed @@ -153,11 +187,12 @@ public interface Structure extends Composite { /** * Inserts a new datatype at the specified offset into this structure. Inserting a component - * will causing any conflicting component to shift down to the extent necessary to avoid a + * will cause any conflicting components to shift down to the extent necessary to avoid a * conflict. * * @param offset the byte offset into the structure where the new datatype is to be inserted. - * @param dataType the datatype to insert. + * @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed + * structure an {@link Undefined1DataType} will be used in its place. * @param length the length to associate with the dataType. For fixed length types a length * <= 0 will use the length of the resolved dataType. * @return the componentDataType created. @@ -171,11 +206,15 @@ public interface Structure extends Composite { /** * Inserts a new datatype at the specified offset into this structure. Inserting a component - * will causing any conflicting component to shift down to the extent necessary to avoid a + * will cause any conflicting components to shift down to the extent necessary to avoid a * conflict. + *

+ * This method does not support bit-field insertions which must use the method + * {@link #insertBitFieldAt(int, int, int, DataType, int, String, String)}. * * @param offset the byte offset into the structure where the new datatype is to be inserted. - * @param dataType the datatype to insert. + * @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed + * structure an {@link Undefined1DataType} will be used in its place. * @param length the length to associate with the dataType. For fixed length types a length * <= 0 will use the length of the resolved dataType. * @param name the field name to associate with this component. @@ -190,25 +229,48 @@ public interface Structure extends Composite { String comment) throws IllegalArgumentException; /** - * Deletes the component containing the specified offset in this structure. If the offset - * corresponds to a bit-field, all bit-fields whose base type group contains the offset will be - * removed. + * Deletes all defined components containing the specified offset in this structure. If the offset + * corresponds to a bit-field or zero-length component (e.g., 0-element array) multiple + * components may be deleted. Bit-fields are only cleared and may leave residual undefined + * components in their place. This method will generally reduce the length of the structure. + * The {@link #clearAtOffset(int)} method should be used for non-packed structures to + * preserve the structure length and placement of other components. * - * @param offset the byte offset into the structure where the datatype is to be deleted. + * @param offset the byte offset into the structure where the component(s) are to be deleted. + * An offset equal to the structure length may be specified to delete any trailing zero-length + * components. + * + * @throws IllegalArgumentException if a negative offset is specified */ - public void deleteAtOffset(int offset); + public void deleteAtOffset(int offset) throws IllegalArgumentException; /** - * Remove all components from this structure (including flex-array), effectively setting the + * Remove all components from this structure, effectively setting the * length to zero. Packing and minimum alignment settings are unaffected. */ public void deleteAll(); + /** + * Clears all defined components containing the specified offset in this structure. If the offset + * corresponds to a bit-field or zero-length component (e.g., 0-element array) multiple + * 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, + * 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. + * + * @param offset the byte offset into the structure where the component(s) are to be deleted. + */ + public void clearAtOffset(int offset); + /** * Clears the defined component at the given component ordinal. Clearing a component within * a non-packed structure causes a defined component to be replaced with a number of undefined - * dataTypes to offset the removal of the defined dataType. In the case of a packed - * structure the component is deleted without backfill. + * 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 + * clearing is always completed without backfill. * * @param ordinal the ordinal of the component to clear. * @throws IndexOutOfBoundsException if component ordinal is out of bounds @@ -216,114 +278,127 @@ public interface Structure extends Composite { public void clearComponent(int ordinal) throws IndexOutOfBoundsException; /** - * Replaces the component at the given component ordinal with a new component of the indicated - * data type. + * Replaces the component at the specified ordinal with a new component using the + * specified datatype, length, name and comment. In the case of a packed structure + * a 1-for-1 replacement will occur. In the case of a non-packed structure certain + * restrictions apply: + *

    + *
  • A zero-length component may only be replaced with another zero-length component.
  • + *
  • If ordinal corresponds to a bit-field, all bit-fields which overlap the specified + * bit-field will be replaced.
  • + *
+ * There must be sufficient space to complete the replacement factoring in the space freed + * by the consumed component(s). If there are no remaining defined components beyond the + * consumed components the structure will expand its length as needed. For a packed structure, this + * method behaves the same as a ordinal-based delete followed by an insert. + *

+ * Datatypes not permitted include {@link FactoryDataType} types, non-sizable + * {@link Dynamic} types, and those which result in a circular direct dependency. + *

+ * NOTE: In general, it is not recommended that this method be used with non-packed + * structures where the replaced component is a bit-field. * * @param ordinal the ordinal of the component to be replaced. - * @param dataType the datatype to insert. - * @param length the length of the dataType to insert. For fixed length types a length <= 0 - * will use the length of the resolved dataType. - * @return the new component - * @throws IllegalArgumentException if the specified data type is not allowed to replace a - * component in this composite data type or an invalid length is specified. For - * example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2 - * component with dt1 since this would cause a cyclic dependency. In addition, any - * attempt to replace an existing bit-field component or specify a - * {@link BitFieldDataType} will produce this error. + * @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed + * structure an {@link Undefined1DataType} will be used in its place. If {@link DataType#DEFAULT} + * is specified for a non-packed structure this is equivelant to {@link #clearComponent(int)}, ignoring + * the length, name and comment arguments. + * @param length component length for containing the specified dataType. A positive length is required + * for sizable {@link Dynamic} datatypes and should be specified as -1 for fixed-length + * datatypes to rely on their resolved size. + * @return the new component. + * @throws IllegalArgumentException may be caused by: 1) invalid offset specified, 2) invalid datatype or + * associated length specified, or 3) insufficient space for replacement. * @throws IndexOutOfBoundsException if component ordinal is out of bounds */ public DataTypeComponent replace(int ordinal, DataType dataType, int length) throws IndexOutOfBoundsException, IllegalArgumentException; /** - * Replaces the component at the given component ordinal with a new component of the indicated - * data type. + * Replaces the component at the specified ordinal with a new component using the + * specified datatype, length, name and comment. In the case of a packed structure + * a 1-for-1 replacement will occur. In the case of a non-packed structure certain + * restrictions apply: + *

    + *
  • A zero-length component may only be replaced with another zero-length component.
  • + *
  • If ordinal corresponds to a bit-field, all bit-fields which overlap the specified + * bit-field will be replaced.
  • + *
+ * There must be sufficient space to complete the replacement factoring in the space freed + * by the consumed component(s). If there are no remaining defined components beyond the + * consumed components the structure will expand its length as needed. For a packed structure, this + * method behaves the same as a ordinal-based delete followed by an insert. + *

+ * Datatypes not permitted include {@link FactoryDataType} types, non-sizable + * {@link Dynamic} types, and those which result in a circular direct dependency. + *

+ * NOTE: In general, it is not recommended that this method be used with non-packed + * structures where the replaced component is a bit-field. * * @param ordinal the ordinal of the component to be replaced. - * @param dataType the datatype to insert. - * @param length the length to associate with the dataType. For fixed length types a length - * <= 0 will use the length of the resolved dataType. - * @param name the field name to associate with this component. - * @param comment the comment to associate with this component. + * @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed + * structure an {@link Undefined1DataType} will be used in its place. If {@link DataType#DEFAULT} + * is specified for a non-packed structure this is equivelant to {@link #clearComponent(int)}, ignoring + * the length, name and comment arguments. + * @param length component length for containing the specified dataType. A positive length is required + * for sizable {@link Dynamic} datatypes and should be specified as -1 for fixed-length + * datatypes to rely on their resolved size. + * @param name the field name to associate with this component or null. + * @param comment the comment to associate with this component or null. * @return the new component. - * @throws IllegalArgumentException if the specified data type is not allowed to replace a - * component in this composite data type or an invalid length is specified. For - * example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2 - * component with dt1 since this would cause a cyclic dependency. In addition, any - * attempt to replace an existing bit-field component or specify a - * {@link BitFieldDataType} will produce this error. + * @throws IllegalArgumentException may be caused by: 1) invalid offset specified, 2) invalid datatype or + * associated length specified, or 3) insufficient space for replacement. * @throws IndexOutOfBoundsException if component ordinal is out of bounds */ public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name, String comment) throws IndexOutOfBoundsException, IllegalArgumentException; /** - * Replaces the component at the specified byte offset with a new component of the indicated - * data type. If the offset corresponds to a bit-field, all bit-fields at that offset will be - * removed and replaced by the specified component. Keep in mind bit-field or any component - * removal must clear sufficient space in a structure with packing disabled to complete - * the replacement. + * Replaces all components containing the specified byte offset with a new component using the + * specified datatype, length, name and comment. If the offset corresponds to a bit-field + * more than one component may be consumed by this replacement. + *

+ * This method may not be used to replace a zero-length component since there may be any number + * of zero-length components at the same offset. If the only defined component(s) at the specified + * offset are zero-length the subsequent undefined will be replaced in the case of a non-packed + * structure. For a packed structure such a case would be treated as an insert as would an offset + * which is not contained within a component. + *

+ * For a non-packed structure a replacement will attempt to consume sufficient + * space within moving other defined components. There must be sufficient space to complete + * the replacement factoring in the space freed by the consumed component(s). When replacing the + * last defined component the structure size will be expanded as needed to fit the new component. + * For a packed If there are no remaining defined components beyond + * the consumed components, or an offset equals to the structure length is specified, the + * structure will expand its length as needed. + *

+ * For a non-packed structure the new component will use the specified offset. In the case of + * packed structure, the actual offset will be determined during a repack. + *

+ * Datatypes not permitted include {@link FactoryDataType} types, non-sizable + * {@link Dynamic} types, and those which result in a circular direct dependency. * - * @param offset the byte offset into the structure where the datatype is to be replaced. - * @param dataType the datatype to insert. - * @param length the length to associate with the dataType. For fixed length types a length - * <= 0 will use the length of the resolved dataType. - * @param name the field name to associate with this component. - * @param comment the comment to associate with this component. + * @param offset the byte offset into the structure where the datatype is to be placed. The specified + * offset must be less than the length of the structure. + * @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed + * structure an {@link Undefined1DataType} will be used in its place. If {@link DataType#DEFAULT} + * is specified for a non-packed structure this is equivelant to clearing all components, + * which contain the specified offset, ignoring the length, name and comment arguments. + * @param length component length for containing the specified dataType. A positive length is required + * for sizable {@link Dynamic} datatypes and should be specified as -1 for fixed-length + * datatypes to rely on their resolved size. + * @param name the field name to associate with this component or null. + * @param comment the comment to associate with this component or null. * @return the new component. - * @throws IllegalArgumentException if the specified data type is not allowed to replace a - * component in this composite data type or an invalid length is specified. For - * example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2 - * component with dt1 since this would cause a cyclic dependency. In addition, any - * attempt to replace an existing bit-field component or specify a - * {@link BitFieldDataType} will produce this error. + * @throws IllegalArgumentException may be caused by: 1) invalid offset specified, 2) invalid datatype or + * associated length specified, or 3) insufficient space for replacement. */ public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name, String comment) throws IllegalArgumentException; - /** - * Determine if a trailing flexible array component has been defined. - * - * @return true if trailing flexible array component has been defined. - */ - public boolean hasFlexibleArrayComponent(); - - /** - * Get the optional trailing flexible array component associated with this structure. - *

- * NOTE: The trailing flexable array may be assigned an incorrect offset - * when packing is enabled and the minimum alignment is specified. In such cases, - * the flex array may be less than the overall structure length. Currently, it is - * assumed the trailing flex array will have an offset equal to the overall - * structure length. - * - * @return optional trailing flexible array component associated with this structure or null if - * not present. - */ - public DataTypeComponent getFlexibleArrayComponent(); - - /** - * Set the optional trailing flexible array component associated with this structure. - * - * @param flexType the flexible array dataType (example: for 'char[0]' the type 'char' should be - * specified) - * @param name component field name or null for default name - * @param comment component comment - * @return updated flexible array component - * @throws IllegalArgumentException if specified flexType is not permitted (e.g., self - * referencing or unsupported type) - */ - public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, - String comment) throws IllegalArgumentException; - - /** - * Remove the optional trailing flexible array component associated with this structure. - */ - public void clearFlexibleArrayComponent(); - /** * Increases the size of the structure by the given amount by adding undefined filler at the - * end of the structure. NOTE: This method only has an affect on structures with packing disabled. + * 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. * @throws IllegalArgumentException if amount < 1 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 2b711f5499..3d2bc3d1d3 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 @@ -26,18 +26,22 @@ import ghidra.util.exception.AssertException; /** * Basic implementation of the structure data type. - * NOTE: Implementation is not thread safe when being modified. + * NOTES: + *

    + *
  • Implementation is not thread safe when being modified.
  • + *
  • For a structure to treated as having a zero-length (see {@link #isZeroLength()}) it + * + *
+ * */ public class StructureDataType extends CompositeDataTypeImpl implements StructureInternal { protected int structLength; private int structAlignment; - protected int numComponents; // excludes optional flexible array component + protected int numComponents; protected List components; - private DataTypeComponentImpl flexibleArrayComponent; - /** * Construct a new structure with the given name and length. The root category will be used. * @@ -130,12 +134,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return ""; } - @Override - public boolean isNotYetDefined() { - return structLength == 0 && flexibleArrayComponent == null && - isDefaultAligned() && !isPackingEnabled(); - } - @Override public int getAlignment() { if (structAlignment > 0) { @@ -152,35 +150,110 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public DataTypeComponent getComponentAt(int offset) { - if (offset >= structLength || offset < 0) { + public DataTypeComponent getDefinedComponentAtOrAfterOffset(int offset) { + if (offset > structLength || offset < 0) { return null; } int index = Collections.binarySearch(components, Integer.valueOf(offset), OffsetComparator.INSTANCE); if (index >= 0) { DataTypeComponent dtc = components.get(index); - if (dtc.isBitFieldComponent()) { - index = backupToFirstComponentContainingOffset(index, offset); - dtc = components.get(index); - } + index = backupToFirstComponentContainingOffset(index, offset); + dtc = components.get(index); return dtc; } - else if (isPackingEnabled()) { + index = -index - 1; + if (index < components.size()) { + return components.get(index); + } + return null; + } + + @Override + public DataTypeComponent getComponentContaining(int offset) { + if (offset > structLength || offset < 0) { return null; } - index = -index - 1; + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + if (index >= 0) { + // return first matching defined component containing offset + DataTypeComponent dtc = components.get(index); + index = backupToFirstComponentContainingOffset(index, offset); + dtc = components.get(index); + return dtc; + } + + if (offset != structLength && !isPackingEnabled()) { + // return undefined component for padding offset within non-packed structure + return generateUndefinedComponent(offset, index); + } + return null; + } + + @Override + public List getComponentsContaining(int offset) { + ArrayList list = new ArrayList<>(); + if (offset > structLength || offset < 0) { + return list; + } + int index = + Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + + boolean hasSizedComponent = false; + if (index >= 0) { + // collect matching defined components containing offset + DataTypeComponentImpl dtc = components.get(index); + index = backupToFirstComponentContainingOffset(index, offset); + while (index < components.size()) { + dtc = components.get(index); + if (!dtc.containsOffset(offset)) { + break; + } + ++index; + hasSizedComponent |= (dtc.getLength() != 0); + list.add(dtc); + } + // transform index for use with generateUndefinedComponent if invoked + index = -index - 1; + } + + if (!hasSizedComponent && offset != structLength && !isPackingEnabled()) { + // generate undefined componentfor padding offset within non-packed structure + // if offset only occupied by zero-length component + list.add(generateUndefinedComponent(offset, index)); + } + return list; + } + + /** + * Generate an undefined component following a binary search across the defined components. + * @param offset the offset within this structure which was searched for + * @param missingComponentIndex the defined component binary search index result (must be negative) + * @return undefined component + */ + private DataTypeComponentImpl generateUndefinedComponent(int offset, + int missingComponentIndex) { + if (missingComponentIndex >= 0) { + throw new AssertException(); + } + missingComponentIndex = -missingComponentIndex - 1; int ordinal = offset; - if (index > 0) { - DataTypeComponent dtc = components.get(index - 1); + if (missingComponentIndex > 0) { + // compute ordinal from previous defined component + DataTypeComponent dtc = components.get(missingComponentIndex - 1); ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); + if (dtc.getLength() == 0) { + ordinal += 1; + } } return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); } @Override public DataTypeComponent getDataTypeAt(int offset) { - DataTypeComponent dtc = getComponentAt(offset); + DataTypeComponent dtc = getComponentContaining(offset); if (dtc != null) { DataType dt = dtc.getDataType(); if (dt instanceof Structure) { @@ -198,7 +271,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public int getLength() { if (structLength == 0) { - return 1; // 0-length datatype not supported + return 1; // positive length required } return structLength; } @@ -222,7 +295,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur OrdinalComparator.INSTANCE); } if (idx >= 0) { - doDelete(idx); + doDeleteWithComponentShift(idx, false); } else { // assume non-packed removal of DEFAULT @@ -233,13 +306,34 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur notifySizeChanged(); } - private void doDelete(int index) { + /** + * Removes a defined component at the specified index without + * any alteration to other components. + * @param index defined component index + * @return the defined component which was removed. + */ + private DataTypeComponentImpl doDelete(int index) { DataTypeComponentImpl dtc = components.remove(index); dtc.getDataType().removeParent(this); + return dtc; + } + + /** + * Removes a defined component at the specified index. + * If this corresponds to a zero-length or bit-field component it will + * be cleared without an offset shift to the remaining components. Removal of + * other component types will result in an offset and ordinal shift + * to the remaining components. + * @param index defined component index + * @param disableOffsetShift if false, and component is not a bit-field, an offset shift + * and possible structure length change will be performed for non-packed structure. + */ + private void doDeleteWithComponentShift(int index, boolean disableOffsetShift) { + DataTypeComponentImpl dtc = doDelete(index); if (isPackingEnabled()) { return; } - int shiftAmount = dtc.isBitFieldComponent() ? 0 : dtc.getLength(); + int shiftAmount = (disableOffsetShift || dtc.isBitFieldComponent()) ? 0 : dtc.getLength(); shiftOffsets(index, -1, -shiftAmount); } @@ -334,30 +428,30 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public DataTypeComponent getComponent(int index) { - if (index == numComponents && flexibleArrayComponent != null) { - return flexibleArrayComponent; + public DataTypeComponentImpl getComponent(int ordinal) { + if (ordinal < 0 || ordinal >= numComponents) { + throw new IndexOutOfBoundsException(ordinal); } - if (index < 0 || index >= numComponents) { - throw new IndexOutOfBoundsException(index); - } - int idx = Collections.binarySearch(components, Integer.valueOf(index), + int index = Collections.binarySearch(components, Integer.valueOf(ordinal), OrdinalComparator.INSTANCE); - if (idx >= 0) { - return components.get(idx); + if (index >= 0) { + return components.get(index); } // assume non-packed DEFAULT int offset = 0; - idx = -idx - 1; - if (idx == 0) { - offset = index; + index = -index - 1; + if (index == 0) { + offset = ordinal; } else { - DataTypeComponent dtc = components.get(idx - 1); - offset = dtc.getEndOffset() + index - dtc.getOrdinal(); + DataTypeComponent dtc = components.get(index - 1); + offset = dtc.getEndOffset() + ordinal - dtc.getOrdinal(); + if (dtc.getLength() == 0) { + --offset; + } } - return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, index, offset); + return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); } @Override @@ -370,14 +464,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return components.size(); } - @Override - protected int getPreferredComponentLength(DataType dataType, int length) { - if (isPackingEnabled() && !(dataType instanceof Dynamic)) { - length = -1; - } - return super.getPreferredComponentLength(dataType, length); - } - @Override public final DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length) { return insertAtOffset(offset, dataType, length, null, null); @@ -411,7 +497,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur checkAncestry(dataType); if ((offset > structLength) && !isPackingEnabled()) { - numComponents = numComponents + (offset - structLength); + numComponents += offset - structLength; structLength = offset; } @@ -445,7 +531,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal, offset, componentName, comment); dataType.addParent(this); - shiftOffsets(index, 1 + additionalShift, dtc.getLength() + additionalShift); + shiftOffsets(index, 1 + additionalShift, length + additionalShift); components.add(index, dtc); repack(false); notifySizeChanged(); @@ -455,7 +541,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public DataTypeComponent add(DataType dataType, int length, String componentName, String comment) { - return doAdd(dataType, length, false, componentName, comment, true); + return doAdd(dataType, length, componentName, comment, true); } /** @@ -469,8 +555,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * @param length maximum component length or -1 to use length of fixed-length dataType after * applying structures data organization as determined by data type manager. If * dataType is Dynamic, a positive length must be specified. - * @param isFlexibleArray if true length is ignored and the trailing flexible array will be set - * based upon the specified fixed-length dataType; * @param componentName component name * @param comment component comment * @param packAndNotify if true perform repack and provide change notification @@ -478,15 +562,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * @throws IllegalArgumentException if the specified data type is not allowed to be added to * this composite data type or an invalid length is specified. */ - private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray, - String componentName, String comment, boolean packAndNotify) + private DataTypeComponent doAdd(DataType dataType, int length, String componentName, + String comment, boolean packAndNotify) throws IllegalArgumentException { - if (isFlexibleArray && isInvalidFlexArrayDataType(dataType)) { - throw new IllegalArgumentException( - "Unsupported flexType: " + dataType.getDisplayName()); - } - dataType = validateDataType(dataType); dataType = dataType.clone(dataMgr); @@ -495,7 +574,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataTypeComponentImpl dtc; if (dataType == DataType.DEFAULT) { - // FIXME: verify - does not appear to modify structure + // Structre will grow by 1-byte below (ignored by packed structure) dtc = new DataTypeComponentImpl(DataType.DEFAULT, this, 1, numComponents, structLength); } @@ -504,38 +583,22 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur int offset = structLength; int ordinal = numComponents; - int componentLength; - if (isFlexibleArray) { - // assume trailing flexible array component - offset = -1; - ordinal = -1; - clearFlexibleArrayComponent(); - componentLength = 0; - } - else { - componentLength = getPreferredComponentLength(dataType, length); - } + int componentLength = getPreferredComponentLength(dataType, length); dtc = new DataTypeComponentImpl(dataType, this, componentLength, ordinal, offset, componentName, comment); dataType.addParent(this); - if (isFlexibleArray) { - flexibleArrayComponent = dtc; - } - else { - components.add(dtc); - } + components.add(dtc); } - if (!isFlexibleArray) { - int structureGrowth = dtc.getLength(); - if (!isPackingEnabled() && length > 0) { - structureGrowth = length; - } - - numComponents++; - structLength += structureGrowth; + int structureGrowth = dtc.getLength(); + if (structureGrowth != 0 && !isPackingEnabled() && length > 0) { + structureGrowth = length; } + + numComponents++; + structLength += structureGrowth; + if (packAndNotify) { repack(false); notifySizeChanged(); @@ -555,12 +618,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public DataTypeComponent insert(int index, DataType dataType, int length, String componentName, + public DataTypeComponent insert(int ordinal, DataType dataType, int length, String componentName, String comment) throws IndexOutOfBoundsException, IllegalArgumentException { - if (index < 0 || index > numComponents) { - throw new IndexOutOfBoundsException(index); + if (ordinal < 0 || ordinal > numComponents) { + throw new IndexOutOfBoundsException(ordinal); } - if (index == numComponents) { + if (ordinal == numComponents) { return add(dataType, length, componentName, comment); } dataType = validateDataType(dataType); @@ -570,12 +633,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur int idx; if (isPackingEnabled()) { - idx = index; + idx = ordinal; } else { // TODO: could improve insertion of bitfield which does not intersect // existing ordinal bitfield at the bit-level - idx = Collections.binarySearch(components, Integer.valueOf(index), + idx = Collections.binarySearch(components, Integer.valueOf(ordinal), OrdinalComparator.INSTANCE); if (idx > 0) { DataTypeComponentImpl existingDtc = components.get(idx); @@ -594,13 +657,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (dataType == DataType.DEFAULT) { // assume non-packed insert of DEFAULT shiftOffsets(idx, 1, 1); - return getComponent(index); + return getComponent(ordinal); } length = getPreferredComponentLength(dataType, length); - int offset = (getComponent(index)).getOffset(); - DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, index, offset, + int offset = (getComponent(ordinal)).getOffset(); + DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal, offset, componentName, comment); dataType.addParent(this); shiftOffsets(idx, 1, dtc.getLength()); @@ -765,10 +828,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } /** - * Backup from specified ordinal to the first component which contains the specified offset. For - * normal components the specified ordinal will be returned, however for bit-fields the ordinal - * of the first bit-field containing the specified offset will be returned. - * + * Backup from specified ordinal to the first component which contains the specified offset. * @param index component ordinal * @param offset offset within structure * @return index of first defined component containing specific offset. @@ -777,35 +837,28 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (index == 0) { return 0; } - DataTypeComponentImpl dtc = components.get(index); - while (index != 0 && dtc.isBitFieldComponent()) { + while (index != 0) { DataTypeComponentImpl previous = components.get(index - 1); if (!previous.containsOffset(offset)) { break; } - dtc = previous; --index; } return index; } /** - * Advance from specified ordinal to the last component which contains the specified offset. For - * normal components the specified ordinal will be returned, however for bit-fields the ordinal - * of the last bit-field containing the specified offset will be returned. - * + * Advance from specified ordinal to the last component which contains the specified offset. * @param index defined component index * @param offset offset within structure * @return index of last defined component containing specific offset. */ private int advanceToLastComponentContainingOffset(int index, int offset) { - DataTypeComponentImpl dtc = components.get(index); - while (index < (components.size() - 1) && dtc.isBitFieldComponent()) { + while (index < (components.size() - 1)) { DataTypeComponentImpl next = components.get(index + 1); if (!next.containsOffset(offset)) { break; } - dtc = next; ++index; } return index; @@ -816,25 +869,25 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } - if (offset >= structLength) { + if (offset > structLength) { return; } + int index = Collections.binarySearch(components, Integer.valueOf(offset), OffsetComparator.INSTANCE); - int offsetDelta = 0; - int ordinalDelta = 0; if (index < 0) { - index = -index - 1; - --ordinalDelta; - offsetDelta = -1; - shiftOffsets(index, ordinalDelta, offsetDelta); + if (offset == structLength) { + return; + } + shiftOffsets(-index - 1, -1, -1); } else { + // delete all components containing offset working backward from last such component index = advanceToLastComponentContainingOffset(index, offset); DataTypeComponentImpl dtc = components.get(index); while (dtc.containsOffset(offset)) { - doDelete(index); + doDeleteWithComponentShift(index, false); if (--index < 0) { break; } @@ -843,7 +896,36 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } repack(false); notifySizeChanged(); - return; + } + + @Override + public void clearAtOffset(int offset) { + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); + } + if (offset > structLength) { + return; + } + + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + if (index < 0) { + return; + } + + // clear all components containing offset working backward from last such component + index = advanceToLastComponentContainingOffset(index, offset); + DataTypeComponentImpl dtc = components.get(index); + while (dtc.containsOffset(offset)) { + doDeleteWithComponentShift(index, true); // updates timestamp + if (--index < 0) { + break; + } + dtc = components.get(index); + } + + repack(false); + notifySizeChanged(); } @Override @@ -860,22 +942,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur int otherLength = struct.isZeroLength() ? 0 : struct.getLength(); if (packing != struct.getStoredPackingValue() || minimumAlignment != struct.getStoredMinimumAlignment() || - isZeroLength() != struct.isZeroLength() || (packing == NO_PACKING && structLength != otherLength)) { return false; } - DataTypeComponent myFlexComp = getFlexibleArrayComponent(); - DataTypeComponent otherFlexComp = struct.getFlexibleArrayComponent(); - if (myFlexComp != null) { - if (otherFlexComp == null || !myFlexComp.isEquivalent(otherFlexComp)) { - return false; - } - } - else if (otherFlexComp != null) { - return false; - } - int myNumComps = components.size(); int otherNumComps = struct.getNumDefinedComponents(); if (myNumComps != otherNumComps) { @@ -913,8 +983,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur // assume no impact to bitfields since base types // should not change size int dtcLen = dtc.getLength(); - int length = dt.getLength(); - if (length <= 0) { + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length < 0) { length = dtcLen; } if (length < dtcLen) { @@ -1064,25 +1134,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur structLength = 0; structAlignment = -1; - if (flexibleArrayComponent != null) { - flexibleArrayComponent.getDataType().removeParent(this); - flexibleArrayComponent = null; - } - this.packing = struct.getStoredPackingValue(); this.minimumAlignment = struct.getStoredMinimumAlignment(); if (struct.isPackingEnabled()) { - doReplaceWithAligned(struct); + doReplaceWithPacked(struct); } else { - doReplaceWithUnaligned(struct); - } - - DataTypeComponent flexComponent = struct.getFlexibleArrayComponent(); - if (flexComponent != null) { - doAdd(flexComponent.getDataType(), 0, true, flexComponent.getFieldName(), - flexComponent.getComment(), false); + doReplaceWithNonPacked(struct); } repack(false); @@ -1090,24 +1149,24 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } // TODO: Rename - private void doReplaceWithAligned(Structure struct) { + private void doReplaceWithPacked(Structure struct) { // assumes components is clear and that alignment characteristics have been set DataTypeComponent[] otherComponents = struct.getDefinedComponents(); for (DataTypeComponent dtc : otherComponents) { DataType dt = dtc.getDataType(); int length = (dt instanceof Dynamic) ? dtc.getLength() : -1; - doAdd(dt, length, false, dtc.getFieldName(), dtc.getComment(), false); + doAdd(dt, length, dtc.getFieldName(), dtc.getComment(), false); } } // TODO: Rename - private void doReplaceWithUnaligned(Structure struct) throws IllegalArgumentException { + private void doReplaceWithNonPacked(Structure struct) throws IllegalArgumentException { // assumes components is clear and that alignment characteristics have been set. - if (struct.isZeroLength()) { + if (struct.isNotYetDefined()) { return; } - structLength = struct.getLength(); + structLength = struct.isZeroLength() ? 0 : struct.getLength(); numComponents = structLength; DataTypeComponent[] otherComponents = struct.getDefinedComponents(); @@ -1116,8 +1175,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataType dt = dtc.getDataType().clone(dataMgr); checkAncestry(dt); - int length = dt.getLength(); - if (length <= 0 || dtc.isBitFieldComponent()) { + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length < 0 || dtc.isBitFieldComponent()) { + // TODO: bitfield truncation/expansion may be an issues if data organization changes length = dtc.getLength(); } else { @@ -1128,9 +1188,11 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur maxOffset = otherComponents[nextIndex].getOffset(); } else { - maxOffset = struct.getLength(); + maxOffset = structLength; + } + if (length > 0) { + length = Math.min(length, maxOffset - dtc.getOffset()); } - length = Math.min(length, maxOffset - dtc.getOffset()); } components.add(new DataTypeComponentImpl(dt, this, length, dtc.getOrdinal(), @@ -1142,11 +1204,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public void dataTypeDeleted(DataType dt) { boolean changed = false; - if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == dt) { - flexibleArrayComponent.getDataType().removeParent(this); - flexibleArrayComponent = null; - changed = true; - } int n = components.size(); for (int i = n - 1; i >= 0; i--) { DataTypeComponentImpl dtc = components.get(i); @@ -1175,33 +1232,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataType newDt = replacementDt; try { validateDataType(replacementDt); - if (replacementDt.getDataTypeManager() != dataMgr) { - replacementDt = replacementDt.clone(dataMgr); - } + replacementDt = replacementDt.clone(dataMgr); checkAncestry(replacementDt); } catch (Exception e) { // 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 (bitfield, aligned structure, etc.) // TODO: failing silently is rather hidden replacementDt = DataType.DEFAULT; } boolean changed = false; - if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == oldDt) { - flexibleArrayComponent.getDataType().removeParent(this); - if (isInvalidFlexArrayDataType(replacementDt)) { - flexibleArrayComponent = null; - Msg.error(this, "Invalid flex array replacement type " + newDt.getName() + - ", removing flex array: " + getPathName()); - } - else { - flexibleArrayComponent.setDataType(replacementDt); - replacementDt.addParent(this); - } - changed = true; - } - for (int i = components.size() - 1; i >= 0; i--) { DataTypeComponentImpl comp = components.get(i); @@ -1243,15 +1284,15 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (changed) { repack(false); - notifySizeChanged(); + notifySizeChanged(); // also handles alignment change } } private void setComponentDataType(DataTypeComponentImpl comp, DataType newDt, int nextIndex) { int oldLen = comp.getLength(); - int len = newDt.getLength(); - if (len < 1) { + int len = DataTypeComponent.usesZeroLengthComponent(newDt) ? 0 : newDt.getLength(); + if (len < 0) { len = oldLen; } @@ -1275,7 +1316,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur comp.setLength(len); shiftOffsets(nextIndex, -bytesNeeded, 0); } - else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure + else if (comp.getOrdinal() == getLastDefinedComponentOrdinal()) { + // we are the last defined component, grow structure doGrowStructure(bytesNeeded - bytesAvailable); comp.setLength(len); shiftOffsets(nextIndex, -bytesNeeded, 0); @@ -1302,148 +1344,327 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public DataTypeComponent replace(int index, DataType dataType, int length, String componentName, + public final DataTypeComponent replace(int index, DataType dataType, int length) { + return replace(index, dataType, length, null, null); + } + + @Override + public DataTypeComponent replace(int ordinal, DataType dataType, int length, String componentName, String comment) throws IndexOutOfBoundsException, IllegalArgumentException { - if (index < 0 || index >= numComponents) { - throw new IndexOutOfBoundsException(index); + if (ordinal < 0 || ordinal >= numComponents) { + throw new IndexOutOfBoundsException(ordinal); } dataType = validateDataType(dataType); - DataTypeComponentImpl origDtc = (DataTypeComponentImpl) getComponent(index); - if (origDtc.isBitFieldComponent()) { - throw new IllegalArgumentException("Bit-field component may not be directly replaced"); - } - - if (dataType == DataType.DEFAULT) { - clearComponent(index); - return getComponent(index); - } - dataType = dataType.clone(dataMgr); checkAncestry(dataType); length = getPreferredComponentLength(dataType, length); - DataTypeComponent replacement = - replaceComponent(origDtc, dataType, length, componentName, comment); + LinkedList replacedComponents = new LinkedList<>(); + int offset; + + int index = ordinal; + if (!isPackingEnabled()) { + index = Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); + } + if (index >= 0) { + // defined component + DataTypeComponentImpl origDtc = components.get(index); + offset = origDtc.getOffset(); + + if (isPackingEnabled() || length == 0) { + // case 1: packed structure or zero-length replacement - do 1-for-1 replacement + replacedComponents.add(origDtc); + } + else if (origDtc.getLength() == 0) { + // case 2: replaced component is zero-length (like-for-like replacement handled by case 1 above + throw new IllegalArgumentException( + "Zero-length component may only be replaced with another zero-length component"); + } + else if (origDtc.isBitFieldComponent()) { + // case 3: replacing bit-field (must replace all bit-fields which overlap) + int minOffset = origDtc.getOffset(); + int maxOffset = origDtc.getEndOffset(); + + replacedComponents.add(origDtc); + + // consume bit-field overlaps before + for (int i = index - 1; i >= 0; i--) { + origDtc = components.get(i); + if (origDtc.getLength() == 0 || !origDtc.containsOffset(minOffset)) { + break; + } + replacedComponents.add(0, origDtc); + } + + // consume bit-field overlaps after + for (int i = index + 1; i < components.size(); i++) { + origDtc = components.get(i); + if (origDtc.getLength() == 0 || !origDtc.containsOffset(maxOffset)) { + break; + } + replacedComponents.add(origDtc); + } + } + else { + // case 4: sized component replacemnt - do 1-for-1 replacement + replacedComponents.add(origDtc); + } + } + else { + // case 5: undefined component replaced (non-packed only) + index = -index - 1; + offset = ordinal; + if (index > 0) { + // use previous defined component to compute undefined offset + DataTypeComponent dtc = components.get(index - 1); + offset = dtc.getEndOffset() + ordinal - dtc.getOrdinal(); + if (dtc.getLength() == 0) { + --offset; + } + } + DataTypeComponentImpl origDtc = + new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); + if (dataType == DataType.DEFAULT) { + return origDtc; // no change + } + replacedComponents.add(origDtc); + } + + DataTypeComponent replaceComponent = + replaceComponents(replacedComponents, dataType, offset, length, componentName, comment); + repack(false); notifySizeChanged(); - return replacement; - } - @Override - public final DataTypeComponent replace(int index, DataType dataType, int length) { - return replace(index, dataType, length, null, null); + return replaceComponent != null ? replaceComponent : getComponent(ordinal); } @Override public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, - String componentName, String comment) throws IllegalArgumentException { + String name, String comment) throws IllegalArgumentException { if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } if (offset >= structLength) { - if (!isPackingEnabled() && offset < getLength()) { - return insertAtOffset(offset, dataType, length, componentName, comment); - } throw new IllegalArgumentException( - "Offset " + offset + " is beyond end of structure (" + getLength() + ")."); + "Offset " + offset + " is beyond end of structure (" + structLength + ")."); } dataType = validateDataType(dataType); - - DataTypeComponentImpl origDtc = (DataTypeComponentImpl) getComponentAt(offset); - if (origDtc.isBitFieldComponent()) { - throw new IllegalArgumentException("Bit-field component may not be directly replaced"); - } - - if (dataType == DataType.DEFAULT) { - int ordinal = origDtc.getOrdinal(); - clearComponent(ordinal); - return getComponent(ordinal); - } - dataType = dataType.clone(dataMgr); checkAncestry(dataType); + LinkedList replacedComponents = new LinkedList<>(); + + DataTypeComponentImpl origDtc = null; + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); + if (index >= 0) { + + // defined component found - advance to last one containing offset + index = advanceToLastComponentContainingOffset(index, offset); + origDtc = components.get(index); + + // case 1: only defined component(s) at offset are zero-length + if (origDtc.getLength() == 0) { + if (isPackingEnabled()) { + // if packed: insert after zero-length component + return insert(index + 1, dataType, length, name, comment); + } + // if non-packed: replace undefined component which immediately follows the zero-length component + replacedComponents.add(new DataTypeComponentImpl(DataType.DEFAULT, this, 1, + origDtc.getOrdinal() + 1, offset)); + } + + // case 2: sized component at offset is bit-field (must replace all bit-fields which contain offset) + else if (origDtc.isBitFieldComponent()) { + replacedComponents.add(origDtc); + for (int i = index - 1; i >= 0; i--) { + origDtc = components.get(i); + if (origDtc.getLength() == 0 || !origDtc.containsOffset(offset)) { + break; + } + replacedComponents.add(0, origDtc); + } + } + + // case 3: normal replacement of sized component + else { + replacedComponents.add(origDtc); + } + } + else { + // defined component not found + index = -index - 1; + + if (isPackingEnabled()) { + // case 4: if replacing padding for packed struction perform insert at correct ordinal + return insert(index, dataType, length, name, comment); + } + + // case 5: replace undefined component at offset - compute undefined component to be replaced + int ordinal = offset; + if (index > 0) { + // use previous defined component to determine ordinal for undefined component + DataTypeComponent dtc = components.get(index - 1); + ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); + } + origDtc = new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); + if (dataType == DataType.DEFAULT) { + return origDtc; // no change + } + replacedComponents.add(origDtc); + } + length = getPreferredComponentLength(dataType, length); - DataTypeComponent replacement = - replaceComponent(origDtc, dataType, length, componentName, comment); + DataTypeComponent replaceComponent = + replaceComponents(replacedComponents, dataType, offset, length, name, comment); repack(false); notifySizeChanged(); - return replacement; + + return replaceComponent != null ? replaceComponent : getComponentContaining(offset); } /** - * Replace the indicated component with a new component containing the specified data type. - * - * @param origDtc the original data type component in this structure. - * @param dataType the data type of the new component - * @param length the length of the new component - * @param componentName the field name of the new component - * @param comment the comment for the new component - * @return the new component or null if the new component couldn't fit. - * @throws IllegalArgumentException if the specified data type is not allowed to replace a - * component in this composite data type. For example, suppose dt1 contains dt2. - * Therefore it is not valid to replace a dt2 component with dt1 since this would - * cause a cyclic dependency. In addition, any attempt to replace an existing - * bit-field component or specify a {@link BitFieldDataType} will produce this - * error. + * Check for available undefined bytes within a non-packed structure for a component + * update with the specified ordinal. + * @param lastOrdinalReplacedOrUpdated the ordinal of a component to be updated + * or the last ordinal with in a sequence of components being replaced. + * @param bytesNeeded number of additional bytes required to complete operation + * @throws IllegalArgumentException if unable to identify/make sufficient space */ - private DataTypeComponent replaceComponent(DataTypeComponentImpl origDtc, DataType dataType, - int length, String componentName, String comment) { + private void checkUndefinedSpaceAvailabilityAfter(int lastOrdinalReplacedOrUpdated, + int bytesNeeded, DataType newDataType, int offset) throws IllegalArgumentException { + if (bytesNeeded <= 0) { + return; + } + int bytesAvailable = getNumUndefinedBytes(lastOrdinalReplacedOrUpdated + 1); + if (bytesAvailable < bytesNeeded) { + if (lastOrdinalReplacedOrUpdated == getLastDefinedComponentOrdinal()) { + growStructure(bytesNeeded - bytesAvailable); + } + else { + throw new IllegalArgumentException("Not enough undefined bytes to fit " + + newDataType.getPathName() + " in structure " + getPathName() + " at offset 0x" + + Integer.toHexString(offset) + "." + " It needs " + + (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."); + } + } + } -// FIXME: Unsure how o support replace operation with bit-fields. Within non-packed structure -// the packing behavior for bit-fields prevents a one-for-one replacement and things may shift -// around which the non-packed structure tries to avoid. Insert and delete are less of a concern -// since movement already can occur, although insert at offset may not retain the offset if it -// interacts with bit-fields. + /** + * Replace the specified components with a new component containing the specified data type. + * If {@link DataType#DEFAULT} is specified as the resolvedDataType only a clear operation + * is performed. + * + * @param origComponents the original sequence of data type components in this structure + * to be replaced. These components must be adjacent components in sequential order. + * If an non-packed undefined component is specified no other component may be included. + * @param dataType the data type of the new component + * @param newOffset offset of replacement component which must fall within origComponents bounds + * @param length the length of the new component + * @param name the field name of the new component + * @param comment the comment for the new component + * @return the new component or null if only a clear operation was performed. + * @throws IllegalArgumentException if unable to identify/make sufficient space + */ + private DataTypeComponent replaceComponents(LinkedList origComponents, + DataType dataType, int newOffset, int length, String name, String comment) + throws IllegalArgumentException { - int ordinal = origDtc.getOrdinal(); - int newOffset = origDtc.getOffset(); - int dtcLength = origDtc.getLength(); + boolean clearOnly = false; + if (dataType == DataType.DEFAULT) { + clearOnly = true; + length = 0; // nothing gets consumed + } - DataTypeComponentImpl newDtc = new DataTypeComponentImpl(dataType, this, length, ordinal, - newOffset, componentName, comment); + DataTypeComponentImpl origFirstDtc = origComponents.getFirst(); + DataTypeComponentImpl origLastDtc = origComponents.getLast(); + int origFirstOrdinal = origFirstDtc.getOrdinal(); + int origLastOrdinal = origLastDtc.getOrdinal(); + int minReplacedOffset = origFirstDtc.getOffset(); + int maxReplacedOffset = origLastDtc.getEndOffset(); - int bytesNeeded = length - dtcLength; - int deltaOrdinal = -bytesNeeded; - if (bytesNeeded > 0) { - int bytesAvailable = getNumUndefinedBytes(ordinal + 1); - if (bytesAvailable < bytesNeeded) { - if (ordinal == getLastDefinedComponentIndex()) { - growStructure(bytesNeeded - bytesAvailable); + // Perform origComponents checks + if (newOffset < minReplacedOffset || newOffset > maxReplacedOffset) { + throw new AssertException("newOffset not contained within origComponents"); + } + if (origComponents.size() > 1) { + int checkOrdinal = origFirstOrdinal; + for (DataTypeComponentImpl origDtc : origComponents) { + if (origDtc.isUndefined()) { + throw new AssertException( + "undefined component within multi-component sequence"); } - else { - throw new IllegalArgumentException("Not enough undefined bytes to fit " + - dataType.getPathName() + " in structure " + getPathName() + - " at offset 0x" + Integer.toHexString(newOffset) + "." + " It needs " + - (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."); + if (origDtc.getOrdinal() != checkOrdinal++) { + throw new AssertException("non-sequential components specified"); } } } - int index = - Collections.binarySearch(components, Integer.valueOf(ordinal), - OrdinalComparator.INSTANCE); - if (index < 0) { - index = -index - 1; + + int leadingUnusedBytes = newOffset - minReplacedOffset; + int newOrdinal = origFirstOrdinal; + if (!isPackingEnabled()) { + newOrdinal += leadingUnusedBytes; // leading unused bytes will become undefined components + } + + // compute space freed by component removal + int origLength = 0; + if (origLastDtc.getLength() != 0) { + origLength = maxReplacedOffset - minReplacedOffset + 1; + } + + if (!clearOnly && !isPackingEnabled()) { + int bytesNeeded = length - origLength + leadingUnusedBytes; + checkUndefinedSpaceAvailabilityAfter(origLastOrdinal, bytesNeeded, dataType, + newOffset); + } + + // determine defined component list insertion point, remove old components + // and insert new component in list + int index; + if (isPackingEnabled()) { + index = newOrdinal; } else { - components.remove(index); - origDtc.getDataType().removeParent(this); + index = Collections.binarySearch(components, Integer.valueOf(origFirstOrdinal), + OrdinalComparator.INSTANCE); } - components.add(index, newDtc); - dataType.addParent(this); - if (deltaOrdinal != 0) { + if (index < 0) { + index = -index - 1; // undefined component replacement + } + else { + for (DataTypeComponentImpl origDtc : origComponents) { + DataTypeComponentImpl dtc = doDelete(index); + if (dtc != origDtc) { + throw new AssertException("component replacement mismatch"); + } + } + } + + DataTypeComponentImpl newDtc = null; + if (!clearOnly) { + // insert new component + newDtc = new DataTypeComponentImpl(dataType, this, length, newOrdinal, + newOffset, name, comment); + components.add(index, newDtc); + } + + // adjust ordinals of trailing components - defer if packing is enabled + if (!isPackingEnabled()) { + int deltaOrdinal = -origComponents.size() + origLength - length; shiftOffsets(index + 1, deltaOrdinal, 0); } return newDtc; } - private int getLastDefinedComponentIndex() { + private int getLastDefinedComponentOrdinal() { if (components.size() == 0) { return 0; } @@ -1490,7 +1711,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur components.clear(); structLength = 0; numComponents = 0; - flexibleArrayComponent = null; notifySizeChanged(); } @@ -1572,43 +1792,4 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur structLength += amount; } - @Override - public boolean hasFlexibleArrayComponent() { - return flexibleArrayComponent != null; - } - - @Override - public DataTypeComponent getFlexibleArrayComponent() { - return flexibleArrayComponent; - } - - private boolean isInvalidFlexArrayDataType(DataType dataType) { - return (dataType == null || dataType == DataType.DEFAULT || - dataType instanceof BitFieldDataType || dataType instanceof Dynamic || - dataType instanceof FactoryDataType); - } - - @Override - public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, - String comment) { - if (isInvalidFlexArrayDataType(flexType)) { - throw new IllegalArgumentException( - "Unsupported flexType: " + flexType.getDisplayName()); - } - DataTypeComponent dtc = doAdd(flexType, 0, true, name, comment, false); - repack(false); - notifySizeChanged(); - return dtc; - } - - @Override - public void clearFlexibleArrayComponent() { - if (flexibleArrayComponent == null) { - return; - } - flexibleArrayComponent = null; - repack(false); - notifySizeChanged(); - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java index 7c35d0b96e..a7c3222a92 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java @@ -26,19 +26,24 @@ import ghidra.util.UniversalID; * Basic implementation for the typedef dataType */ public class TypedefDataType extends GenericDataType implements TypeDef { - private final static long serialVersionUID = 1; private DataType dataType; private boolean deleted = false; + /** + * Construct a new typedef within the root category + * @param name name of this typedef + * @param dt data type that is being typedef'ed (may not be null) + */ public TypedefDataType(String name, DataType dt) { this(CategoryPath.ROOT, name, dt, null); } /** * Construct a new typedef. - * @param name name to use as the alias - * @param dt data type that is being typedef'ed + * @param path category path for this datatype + * @param name name of this typedef + * @param dt data type that is being typedef'ed (may not be null) */ public TypedefDataType(CategoryPath path, String name, DataType dt) { this(path, name, dt, null); @@ -46,8 +51,10 @@ public class TypedefDataType extends GenericDataType implements TypeDef { /** * Construct a new typedef. - * @param name name to use as the alias - * @param dt data type that is being typedef'ed + * @param path category path for this datatype + * @param name name of this typedef + * @param dt data type that is being typedef'ed (may not be null) + * @param dtm the data type manager associated with this data type. This can be null. */ public TypedefDataType(CategoryPath path, String name, DataType dt, DataTypeManager dtm) { super(path, name, dtm); @@ -58,15 +65,15 @@ public class TypedefDataType extends GenericDataType implements TypeDef { /** * Construct a new typedef. - * @param path the category path indicating where this data type is located. - * @param name the name of the new structure + * @param path category path for this datatype + * @param name name of this typedef + * @param dt data type that is being typedef'ed (may not be null) * @param universalID the id for the data type * @param sourceArchive the source archive for this data type * @param lastChangeTime the last time this data type was changed * @param lastChangeTimeInSourceArchive the last time this data type was changed in * its source archive. * @param dtm the data type manager associated with this data type. This can be null. - * Also, the data type manager may not contain this actual data type. */ public TypedefDataType(CategoryPath path, String name, DataType dt, UniversalID universalID, SourceArchive sourceArchive, long lastChangeTime, long lastChangeTimeInSourceArchive, @@ -98,11 +105,6 @@ public class TypedefDataType extends GenericDataType implements TypeDef { return getName(); } - @Override - public boolean isNotYetDefined() { - return dataType.isNotYetDefined(); - } - @Override public boolean hasLanguageDependantLength() { return dataType.hasLanguageDependantLength(); @@ -230,6 +232,9 @@ public class TypedefDataType extends GenericDataType implements TypeDef { if (oldDt.getLength() != newDt.getLength()) { notifySizeChanged(); } + else if (oldDt.getAlignment() != newDt.getAlignment()) { + notifyAlignmentChanged(); + } } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java index 9f1df95ab7..ac836235ae 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java @@ -97,11 +97,6 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna return ""; } - @Override - public boolean isNotYetDefined() { - return unionLength == 0 && isDefaultAligned() && !isPackingEnabled(); - } - @Override public DataTypeComponent getComponent(int ordinal) { return components.get(ordinal); @@ -243,7 +238,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna @Override public int getLength() { if (unionLength == 0) { - return 1; // 0-length datatype not supported + return 1; // positive length required } return unionLength; } @@ -491,12 +486,11 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna boolean changed = false; for (DataTypeComponentImpl dtc : components) { if (dtc.getDataType() == dt) { - int length = dt.getLength(); - if (length <= 0) { - length = dtc.getLength(); + int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); + if (length >= 0 && length != dtc.getLength()) { + dtc.setLength(length); + changed = true; } - dtc.setLength(length); - changed = true; } } if (changed && !repack(true) && isPackingEnabled()) { @@ -547,13 +541,15 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna remove = true; } else { - oldDt.removeParent(this); - dtc.setDataType(replacementDt); - replacementDt.addParent(this); - int len = replacementDt.getLength(); - if (len > 0) { - dtc.setLength(len); + int len = + DataTypeComponent.usesZeroLengthComponent(newDt) ? 0 : newDt.getLength(); + if (len < 0) { + len = dtc.getLength(); } + oldDt.removeParent(this); + dtc.setLength(len); + dtc.setDataType(replacementDt); + replacementDt.addParent(this); changed = true; } } @@ -567,7 +563,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna } if (changed) { repack(false); - notifySizeChanged(); + notifySizeChanged(); // also handles alignment change } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageService.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageService.java index 3fce1b14e0..f8bc31095b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageService.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageService.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.model.lang; import java.util.List; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java index a253649988..0650651c83 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Data.java @@ -29,6 +29,8 @@ import ghidra.program.model.symbol.Reference; */ public interface Data extends CodeUnit, Settings { + // TODO: Fix javadocs !! + /** * Returns the value of the data item. The value may be an address, a scalar, * register or null if no value. @@ -195,30 +197,52 @@ public interface Data extends CodeUnit, Settings { public int getNumComponents(); /** - * Return the immediate child component that contains the byte - * at the given offset. + * Return the first immediate child component that contains the byte + * at the given offset. It is important to note that with certain + * datatypes there may be more than one component containing the specified offset + * (see {@link #getComponentsContaining(int)}). + * * @param offset the amount to add to this data items address to get the * address of the requested data item. + * @return first data component containing offset or null + * @deprecated method name has been changed to better reflect behavior. The method + * {@link #getComponentContaining(int)} should be used instead. */ + @Deprecated Data getComponentAt(int offset); + /** + * RReturn the first immediate child component that contains the byte + * at the given offset. It is important to note that with certain + * datatypes there may be more than one component containing the specified offset + * (see {@link #getComponentsContaining(int)}). + * + * @param offset the amount to add to this data items address to get the + * @return first data component containing offset or null + * address of the requested data item. + */ + Data getComponentContaining(int offset); + /** * Returns a list of all the immediate child components that contain the byte at the * given offset. *

- * For a union, this will return all the components (if the offset is 0). For a structure, - * this will be either a single non bit field element or a list of bit field elements. + * For a union, this will return all the components (if the offset is 0). The presence of bit-fields + * or zero-length components may cause multiple components to be returned. * @param offset the amount to add to this data items address to get the * address of the requested data item. * @return a list of all the immediate child components that contain the byte at the - * given offset. + * given offset or null if offset is out of bounds. */ List getComponentsContaining(int offset); /** - * Returns the primitive component that is at this offset. This is useful - * for data items are made up of multiple layers of other data items. This - * method immediately goes to the lowest level data item. + * Returns the primitive component containing this offset (i.e., one that does not + * have sub-components). This is useful for data items which are made up of multiple + * layers of other data items. This method immediately goes to the lowest level data item. + * If the minimum offset of a component is specified, the only first component containing + * the offset will be considered (e.g., 0-element array). + * @return primitive component containing this offset */ Data getPrimitiveAt(int offset); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java index 44fbe772be..3057bb9b34 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java @@ -527,6 +527,11 @@ public class DataStub implements Data { throw new UnsupportedOperationException(); } + @Override + public Data getComponentContaining(int offset) { + throw new UnsupportedOperationException(); + } + @Override public List getComponentsContaining(int offset) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java index 1e2b77b8a3..62f25d789e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java @@ -253,55 +253,112 @@ public class VariableUtilities { } /** - * Perform variable datatype checks - * @param dataType datatype to be checked or null to produce suitable Undefined type - * @param voidOK true if the zero-sized void data type is permitted - * @param defaultSize datatype size to be used if specified datatype is null - * @param program program which corresponds to this variable - * @return checked datatype (could be new instance) - * @throws InvalidInputException + * Check the specified datatype for use as a return, parameter or variable type. It may + * not be suitable for other uses. The following datatypes will be mutated into a default pointer datatype: + *

    + *
  • Function definition datatype
  • + *
  • An unsized/zero-element array
  • + *
+ * @param dataType datatype to be checked + * @param voidOK true if checking return datatype and void is allow, else false. + * @param defaultSize Undefined datatype size to be used if specified datatype is null. A value less than 1 + * will result in the DEFAULT data type being returned (i.e., "undefined"). + * @param dtMgr target datatype manager (null permitted which will adopt default data organization) + * @return cloned/mutated datatype suitable for function parameters and variables (including function return data type). + * @throws InvalidInputException if an unacceptable datatype was specified */ public static DataType checkDataType(DataType dataType, boolean voidOK, int defaultSize, - Program program) throws InvalidInputException { - if (dataType instanceof BitFieldDataType) { - throw new InvalidInputException("Bitfields not supported for variable"); - } + DataTypeManager dtMgr) throws InvalidInputException { + if (dataType == null) { - if (voidOK) { - return VoidDataType.dataType; - } dataType = Undefined.getUndefinedDataType(defaultSize); } - else if (dataType.hasLanguageDependantLength()) { - // A clone is done to ensure that any affects of the data organization - // are properly reflected in the sizing of the datatype - dataType = dataType.clone(program.getDataTypeManager()); + else if (dataType instanceof BitFieldDataType) { + throw new InvalidInputException("Bitfield not permitted"); } - else if (dataType instanceof FunctionDefinition || (dataType instanceof TypeDef && - ((TypeDef) dataType).getBaseDataType() instanceof FunctionDefinition)) { - dataType = new PointerDataType(dataType, program.getDataTypeManager()); + else if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) { + throw new InvalidInputException( + "Dynamic and Factory data types are not permitted: " + dataType.getName()); + } + + DataType baseType = dataType; + if (baseType instanceof TypeDef) { + baseType = ((TypeDef) baseType).getBaseDataType(); + } + + if (baseType instanceof FunctionDefinition) { + dataType = new PointerDataType(dataType, dtMgr); + } + else if (baseType instanceof Array) { + // TODO: Uncertain if typedefs or multi-dimensional arrays should be handled? + Array a = (Array) baseType; + if (a.getNumElements() == 0) { + // convert unsized/zero-length array to pointer + dataType = new PointerDataType(a.getDataType(), dtMgr); + } + } + + // A clone is done to ensure that any affects of the data organization + // are properly reflected in the sizing of the datatype. + // NOTE: This will not properly handle composites since a deep-clone is not performed. + dataType = dataType.clone(dtMgr); + + if (baseType instanceof VoidDataType) { + if (!voidOK) { + throw new InvalidInputException( + "The void type is not permitted - allowed for function return use only"); + } + return dataType; } if (dataType.getLength() <= 0) { - if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) { - throw new InvalidInputException( - "Dynamic or Factory data-type not allowed: " + dataType.getName()); - } - DataType baseType = dataType; - if (baseType instanceof TypeDef) { - baseType = ((TypeDef) baseType).getBaseDataType(); - } - if (baseType instanceof Structure) { - // ignore - allow 0-sized structures - } - else if (!(baseType instanceof VoidDataType) || !voidOK) { - throw new InvalidInputException( - "Expected fixed-length data type with positive size"); - } + // Unexpected condition - only dynamic types are expected to have negative length and + // none should report 0 has a length. + throw new IllegalArgumentException("Unsupported data type length (" + + dataType.getLength() + "): " + dataType.getName()); } return dataType; } + /** + * Check the specified datatype for use as a return, parameter or variable type. It may + * not be suitable for other uses. The following datatypes will be mutated into a default pointer datatype: + *
    + *
  • Function definition datatype
  • + *
  • An unsized/zero-element array
  • + *
+ * @param dataType datatype to be checked + * @param voidOK true if checking return datatype and void is allow, else false. + * @param defaultSize Undefined datatype size to be used if specified datatype is null. A value less than 1 + * will result in the DEFAULT data type being returned (i.e., "undefined"). + * @param program target program + * @return cloned/mutated datatype suitable for function parameters and variables (including function return data type). + * @throws InvalidInputException if an unacceptable datatype was specified + */ + public static DataType checkDataType(DataType dataType, boolean voidOK, int defaultSize, + Program program) throws InvalidInputException { + return checkDataType(dataType, voidOK, defaultSize, program.getDataTypeManager()); + } + + /** + * Check the specified datatype for use as a return, parameter or variable type. It may + * not be suitable for other uses. The following datatypes will be mutated into a default pointer datatype: + *
    + *
  • Function definition datatype
  • + *
  • An unsized/zero-element array
  • + *
+ * @param dataType datatype to be checked. If null is specified the DEFAULT datatype will be + * returned. + * @param voidOK true if checking return datatype and void is allow, else false. + * @param dtMgr target datatype manager (null permitted which will adopt default data organization) + * @return cloned/mutated datatype suitable for function parameters and variables (including function return data type). + * @throws InvalidInputException if an unacceptable datatype was specified + */ + public static DataType checkDataType(DataType dataType, boolean voidOK, DataTypeManager dtMgr) + throws InvalidInputException { + return checkDataType(dataType, voidOK, -1, dtMgr); + } + /** * Perform resize variable storage to desired newSize. This method has limited ability to grow * storage if current storage does not have a stack component or if other space constraints diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java index fd0f6bd8bb..4c4a6c1595 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java @@ -416,7 +416,8 @@ public class PcodeDataTypeManager { buildTypeRef(resBuf, ptrto, ptrto.getLength()); } } - else if (type instanceof Array) { + else if ((type instanceof Array) && !type.isZeroLength()) { + // TODO: Zero-element arrays not yet supported SpecXmlUtils.encodeStringAttribute(resBuf, "name", ""); int sz = type.getLength(); if (sz == 0) { @@ -443,8 +444,8 @@ public class PcodeDataTypeManager { resBuf.append(">\n"); DataTypeComponent[] comps = ((Structure) type).getDefinedComponents(); for (DataTypeComponent comp : comps) { - if (comp.isBitFieldComponent()) { - // TODO: bitfields are not yet supported by decompiler + if (comp.isBitFieldComponent() || comp.getLength() == 0) { + // TODO: bitfields, zero-length components and zero-element arrays are not yet supported by decompiler continue; } resBuf.append("\n"); } - // TODO: trailing flexible array component not yet supported } else if (type instanceof Enum) { appendNameIdAttributes(resBuf, type); diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java index 770428bbed..4ee906eb3f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/DataTypeWriterTest.java @@ -15,7 +15,7 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; import java.io.StringWriter; @@ -24,7 +24,7 @@ import org.junit.*; import generic.test.AbstractGTest; import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; public class DataTypeWriterTest extends AbstractGTest { @@ -53,7 +53,7 @@ public class DataTypeWriterTest extends AbstractGTest { @Test public void testTypeDef() throws IOException, CancelledException { TypeDef typedef = new TypedefDataType("BOB", new CharDataType()); - dtWriter.write(typedef, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(typedef, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef char BOB;" + EOL + EOL; assertEquals(expected, actual); @@ -62,7 +62,7 @@ public class DataTypeWriterTest extends AbstractGTest { @Test public void testTypeDef2() throws IOException, CancelledException { TypeDef typedef = new TypedefDataType("unsigned int", new DWordDataType()); - dtWriter.write(typedef, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(typedef, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = ""; assertEquals(expected, actual); @@ -71,7 +71,7 @@ public class DataTypeWriterTest extends AbstractGTest { @Test public void testTypeDef3() throws IOException, CancelledException { TypeDef typedef = new TypedefDataType("const float", new DWordDataType()); - dtWriter.write(typedef, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(typedef, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = ""; assertEquals(expected, actual); @@ -89,7 +89,7 @@ public class DataTypeWriterTest extends AbstractGTest { TypeDef typedef = new TypedefDataType("static const " + pointer2.getDisplayName(), pointer2); - dtWriter.write(typedef, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(typedef, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = ""; assertEquals(expected, actual); @@ -103,7 +103,7 @@ public class DataTypeWriterTest extends AbstractGTest { enumm.add("C", 2); enumm.add("D", 3); enumm.add("E", 4); - dtWriter.write(enumm, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(enumm, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef enum myEnum {" + EOL + " A=0," + EOL + " B=1," + EOL + " C=2," + EOL + " D=3," + EOL + " E=4" + EOL + "} myEnum;" + EOL + EOL; @@ -118,7 +118,7 @@ public class DataTypeWriterTest extends AbstractGTest { enumm.add("C", 16); enumm.add("D", 32); enumm.add("E", 254); - dtWriter.write(enumm, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(enumm, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef enum myEnum {" + EOL + " A=4," + EOL + " B=8," + EOL + " C=16," + EOL + " D=32," + EOL + " E=254" + EOL + "} myEnum;" + EOL + EOL; @@ -130,6 +130,8 @@ public class DataTypeWriterTest extends AbstractGTest { Structure struct = new StructureDataType("MyStruct", 0); struct.setDescription("this is my structure"); struct.add(new CharDataType(), "myChar", "this is a character"); + struct.add(new ArrayDataType(CharDataType.dataType, 5, -1), "myCharArray", + "this is a character array"); struct.add(new ByteDataType(), "myByte", "this is a byte"); struct.add(new WordDataType(), "myWord", "this is a word"); struct.add(new DWordDataType(), "myDWord", "this is a dword"); @@ -138,14 +140,16 @@ public class DataTypeWriterTest extends AbstractGTest { struct.add(new DoubleDataType(), "myDouble", "this is a double"); struct.add(PointerDataType.getPointer(new FloatDataType(), 4), "myFloatPointer", "this is a float pointer"); - struct.setFlexibleArrayComponent(new CharDataType(), "myFlexArray", "this is a flex array"); - dtWriter.write(struct, TaskMonitorAdapter.DUMMY_MONITOR); + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), 0, "myFlexArray", + "this is a flex array"); + dtWriter.write(struct, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef struct MyStruct MyStruct, *PMyStruct;" + EOL + EOL + "typedef unsigned char byte;" + EOL + "typedef unsigned short word;" + EOL + "typedef unsigned int dword;" + EOL + "typedef unsigned long long qword;" + EOL + "struct MyStruct { /* this is my structure */" + EOL + " char myChar; /* this is a character */" + EOL + + " char myCharArray[5]; /* this is a character array */" + EOL + " byte myByte; /* this is a byte */" + EOL + " word myWord; /* this is a word */" + EOL + " dword myDWord; /* this is a dword */" + EOL + @@ -153,7 +157,7 @@ public class DataTypeWriterTest extends AbstractGTest { " float myFloat; /* this is a float */" + EOL + " double myDouble; /* this is a double */" + EOL + " float * myFloatPointer; /* this is a float pointer */" + EOL + - " char[0] myFlexArray; /* this is a flex array */" + EOL + "};" + EOL + EOL; + " char myFlexArray[0]; /* this is a flex array */" + EOL + "};" + EOL + EOL; assertEquals(expected, actual); } @@ -168,15 +172,15 @@ public class DataTypeWriterTest extends AbstractGTest { struct.add(new FloatDataType()); struct.add(new DoubleDataType()); struct.add(PointerDataType.getPointer(new FloatDataType(), 4)); - dtWriter.write(struct, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(struct, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef struct MyBasicStruct MyBasicStruct, *PMyBasicStruct;" + EOL + EOL + "typedef unsigned char byte;" + EOL + "typedef unsigned short word;" + EOL + "typedef unsigned int dword;" + EOL + "typedef unsigned long long qword;" + EOL + - "struct MyBasicStruct {" + EOL + " char field_0x0;" + EOL + " byte field_0x1;" + - EOL + " word field_0x2;" + EOL + " dword field_0x4;" + EOL + - " qword field_0x8;" + EOL + " float field_0x10;" + EOL + - " double field_0x14;" + EOL + " float * field_0x1c;" + EOL + "};" + EOL + EOL; + "struct MyBasicStruct {" + EOL + " char field0_0x0;" + EOL + " byte field1_0x1;" + + EOL + " word field2_0x2;" + EOL + " dword field3_0x4;" + EOL + + " qword field4_0x8;" + EOL + " float field5_0x10;" + EOL + + " double field6_0x14;" + EOL + " float * field7_0x1c;" + EOL + "};" + EOL + EOL; assertEquals(expected, actual); } @@ -196,7 +200,7 @@ public class DataTypeWriterTest extends AbstractGTest { "this is a outer inner structure"); outerStructure.add(new CharDataType(), "myOuterChar", "this is a outer character"); - dtWriter.write(outerStructure, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(outerStructure, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -230,7 +234,7 @@ public class DataTypeWriterTest extends AbstractGTest { outerUnion.add(innerStructure, "myOuterInnerStructure", "this is a outer inner structure"); outerUnion.add(new CharDataType(), "myOuterChar", "this is a outer character"); - dtWriter.write(outerUnion, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(outerUnion, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -256,12 +260,12 @@ public class DataTypeWriterTest extends AbstractGTest { struct.add(PointerDataType.getPointer(struct, 4)); struct.add(new DoubleDataType()); - dtWriter.write(struct, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(struct, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef struct MySelfRefStruct MySelfRefStruct, *PMySelfRefStruct;" + EOL + EOL + "typedef unsigned short word;" + EOL + "struct MySelfRefStruct {" + EOL + - " word field_0x0;" + EOL + " struct MySelfRefStruct * field_0x2;" + EOL + - " double field_0x6;" + EOL + "};" + EOL + EOL; + " word field0_0x0;" + EOL + " struct MySelfRefStruct * field1_0x2;" + EOL + + " double field2_0x6;" + EOL + "};" + EOL + EOL; assertEquals(expected, actual); } @@ -279,7 +283,7 @@ public class DataTypeWriterTest extends AbstractGTest { union.add(new DoubleDataType(), "myDouble", "this is a double"); union.add(PointerDataType.getPointer(new FloatDataType(), 4), "myFloatPointer", "this is a float pointer"); - dtWriter.write(union, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(union, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef union MyUnion MyUnion, *PMyUnion;" + EOL + EOL + "typedef unsigned char byte;" + EOL + "typedef unsigned short word;" + EOL + @@ -312,7 +316,7 @@ public class DataTypeWriterTest extends AbstractGTest { outerUnion.add(innerUnion, "myOuterInnerUnion", "this is a outer inner union"); outerUnion.add(new CharDataType(), "myOuterChar", "this is a outer character"); - dtWriter.write(outerUnion, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(outerUnion, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -346,7 +350,7 @@ public class DataTypeWriterTest extends AbstractGTest { outerStructure.add(innerUnion, "myOuterInnerUnion", "this is a outer inner union"); outerStructure.add(new CharDataType(), "myOuterChar", "this is a outer character"); - dtWriter.write(outerStructure, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(outerStructure, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -380,7 +384,7 @@ public class DataTypeWriterTest extends AbstractGTest { struct.add(PointerDataType.getPointer(new FloatDataType(), 4), "myFloatPointer", "this is my float pointer"); - dtWriter.write(struct, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(struct, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -408,7 +412,7 @@ public class DataTypeWriterTest extends AbstractGTest { struct.add(PointerDataType.getPointer(new FloatDataType(), 4), "myFloatPointer", "this is my float pointer"); - dtWriter.write(struct, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(struct, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -436,7 +440,7 @@ public class DataTypeWriterTest extends AbstractGTest { union.add(PointerDataType.getPointer(new FloatDataType(), 4), "myFloatPointer", "this is my float pointer"); - dtWriter.write(union, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(union, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); @@ -458,7 +462,7 @@ public class DataTypeWriterTest extends AbstractGTest { union.add(PointerDataType.getPointer(union, 4)); union.add(new DoubleDataType()); - dtWriter.write(union, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(union, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = "typedef union MySelfRefUnion MySelfRefUnion, *PMySelfRefUnion;" + EOL + EOL + "typedef unsigned short word;" + EOL + "union MySelfRefUnion {" + EOL + @@ -474,20 +478,20 @@ public class DataTypeWriterTest extends AbstractGTest { // Only base type is written-out - not pointer Pointer ptr = PointerDataType.getPointer(null, null); - dtWriter.write(ptr, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(ptr, TaskMonitor.DUMMY); String actual = writer.getBuffer().toString(); String expected = ""; assertEquals(expected, actual); ptr = PointerDataType.getPointer(DataType.DEFAULT, null); - dtWriter.write(ptr, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(ptr, TaskMonitor.DUMMY); actual = writer.getBuffer().toString(); expected += "typedef unsigned char undefined;" + EOL + EOL; assertEquals(expected, actual); TypeDef typedef = new TypedefDataType("BOB", new CharDataType()); ptr = PointerDataType.getPointer(typedef, null); - dtWriter.write(ptr, TaskMonitorAdapter.DUMMY_MONITOR); + dtWriter.write(ptr, TaskMonitor.DUMMY); actual = writer.getBuffer().toString(); expected += "typedef char BOB;" + EOL + EOL; assertEquals(expected, actual); 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 fe8710697b..59654e57c4 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 @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import static org.junit.Assert.*; +import java.util.List; + import org.junit.*; import com.google.common.collect.Sets; @@ -50,11 +49,12 @@ public class StructureDBTest extends AbstractGTest { struct.add(new WordDataType(), null, "Comment2"); struct.add(new DWordDataType(), "field3", null); struct.add(new ByteDataType(), "field4", "Comment4"); + } private void transitionToBigEndian() { - Structure structClone = (Structure) struct.clone(null); + Structure structClone = struct.clone(null); dataMgr.remove(struct, TaskMonitor.DUMMY); DataOrganizationImpl dataOrg = (DataOrganizationImpl) dataMgr.getDataOrganization(); @@ -411,7 +411,7 @@ public class StructureDBTest extends AbstractGTest { } - // test inserting at offset 1 + // test inserting at offset 2 @Test public void testInsertAtOffset2() { struct.insertAtOffset(2, new FloatDataType(), 4); @@ -438,6 +438,72 @@ public class StructureDBTest extends AbstractGTest { assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); } + + @Test + public void testInsertWithZeroArrayAtOffset() { + struct.insertAtOffset(2, FloatDataType.dataType, -1); + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + struct.insertAtOffset(2, zeroArray, -1); + assertEquals(13, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(6, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[1].getDataType())); + + assertEquals(2, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertEquals(FloatDataType.class, comps[2].getDataType().getClass()); + + assertEquals(6, comps[3].getOffset()); + assertEquals(4, comps[3].getOrdinal()); + assertEquals(WordDataType.class, comps[3].getDataType().getClass()); + + assertEquals(8, comps[4].getOffset()); + assertEquals(5, comps[4].getOrdinal()); + assertEquals(DWordDataType.class, comps[4].getDataType().getClass()); + + } + + @Test + public void testInsertWithZeroArrayAtOffset2() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + struct.insertAtOffset(2, zeroArray, -1); + struct.insertAtOffset(2, FloatDataType.dataType, -1); + assertEquals(13, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(6, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertEquals(FloatDataType.class, comps[1].getDataType().getClass()); + + assertEquals(6, comps[2].getOffset()); + assertEquals(3, comps[2].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[2].getDataType())); + + assertEquals(6, comps[3].getOffset()); + assertEquals(4, comps[3].getOrdinal()); + assertEquals(WordDataType.class, comps[3].getDataType().getClass()); + + assertEquals(8, comps[4].getOffset()); + assertEquals(5, comps[4].getOrdinal()); + assertEquals(DWordDataType.class, comps[4].getDataType().getClass()); + + } @Test public void testInsertAtOffsetPastEnd() { @@ -452,7 +518,8 @@ public class StructureDBTest extends AbstractGTest { struct.delete(2); // remove dword to verify flex array alignment below - struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", "FlexComment"); + DataTypeComponent flexDtc = + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "flex", "FlexComment"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + @@ -461,12 +528,13 @@ public class StructureDBTest extends AbstractGTest { " 0 byte 1 field1 \"Comment1\"\n" + " 2 word 2 null \"Comment2\"\n" + " 4 byte 1 field4 \"Comment4\"\n" + - " char[0] 0 flex \"FlexComment\"\n" + + " 5 char[0] 0 flex \"FlexComment\"\n" + "}\n" + "Size = 6 Actual Alignment = 2", struct); //@formatter:on - struct.setFlexibleArrayComponent(IntegerDataType.dataType, "flex", "FlexComment"); + struct.replace(flexDtc.getOrdinal(), new ArrayDataType(IntegerDataType.dataType, 0, -1), 0, + "flex", "FlexComment"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + @@ -475,7 +543,7 @@ public class StructureDBTest extends AbstractGTest { " 0 byte 1 field1 \"Comment1\"\n" + " 2 word 2 null \"Comment2\"\n" + " 4 byte 1 field4 \"Comment4\"\n" + - " int[0] 0 flex \"FlexComment\"\n" + + " 8 int[0] 0 flex \"FlexComment\"\n" + "}\n" + "Size = 8 Actual Alignment = 4", struct); //@formatter:on @@ -497,7 +565,7 @@ public class StructureDBTest extends AbstractGTest { "pack()\n" + "Structure Test {\n" + " 0 int:3(0) 1 bf1 \"bf1Comment\"\n" + - " 4 int:0(0) 1 \"zero bitfield 1\"\n" + + " 4 int:0(0) 0 \"zero bitfield 1\"\n" + " 4 int:3(0) 1 bf2 \"bf1Comment\"\n" + " 5 byte 1 field1 \"Comment1\"\n" + " 6 word 2 null \"Comment2\"\n" + @@ -513,14 +581,14 @@ public class StructureDBTest extends AbstractGTest { "pack()\n" + "Structure Test {\n" + " 0 int:3(0) 1 bf1 \"bf1Comment\"\n" + - " 4 int:0(0) 1 \"zero bitfield 1\"\n" + - " 8 int:0(0) 1 \"zero bitfield 2\"\n" + - " 8 int:3(0) 1 bf2 \"bf1Comment\"\n" + - " 9 byte 1 field1 \"Comment1\"\n" + - " 10 word 2 null \"Comment2\"\n" + - " 12 byte 1 field4 \"Comment4\"\n" + + " 4 int:0(0) 0 \"zero bitfield 1\"\n" + + " 4 int:0(0) 0 \"zero bitfield 2\"\n" + + " 4 int:3(0) 1 bf2 \"bf1Comment\"\n" + + " 5 byte 1 field1 \"Comment1\"\n" + + " 6 word 2 null \"Comment2\"\n" + + " 8 byte 1 field4 \"Comment4\"\n" + "}\n" + - "Size = 16 Actual Alignment = 4", struct); + "Size = 12 Actual Alignment = 4", struct); //@formatter:on } @@ -726,7 +794,7 @@ public class StructureDBTest extends AbstractGTest { "Structure Test {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + - " 2 int:0(0) 1 \"zero bitfield\"\n" + // field name discarded + " 2 int:0(0) 0 \"zero bitfield\"\n" + // field name discarded " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + " 2 int:3(3) 1 bf2 \"bf2Comment\"\n" + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + @@ -838,7 +906,7 @@ public class StructureDBTest extends AbstractGTest { "Structure Test {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + - " 2 int:0(7) 1 \"zero bitfield\"\n" + // field name discarded + " 2 int:0(7) 0 \"zero bitfield\"\n" + // field name discarded " 2 int:3(5) 1 bf1 \"bf1Comment\"\n" + " 2 int:3(2) 1 bf2 \"bf2Comment\"\n" + " 2 int:15(3) 3 bf3 \"bf3Comment\"\n" + @@ -1071,11 +1139,17 @@ public class StructureDBTest extends AbstractGTest { @Test public void testDeleteFlexArrayDependency() { - struct.setFlexibleArrayComponent(IntegerDataType.dataType, "flex", "FlexComment"); + assertEquals(4, struct.getNumComponents()); + assertEquals(8, struct.getLength()); - dataMgr.remove(struct.getFlexibleArrayComponent().getDataType(), TaskMonitor.DUMMY); + struct.add(new ArrayDataType(IntegerDataType.dataType, 0, -1), "flex", "FlexComment"); + assertEquals(5, struct.getNumComponents()); + assertEquals(8, struct.getLength()); - assertNull(struct.getFlexibleArrayComponent()); + dataMgr.remove(dataMgr.resolve(IntegerDataType.dataType, null), TaskMonitor.DUMMY); + + assertEquals(4, struct.getNumComponents()); + assertEquals(8, struct.getLength()); } @Test @@ -1131,7 +1205,7 @@ public class StructureDBTest extends AbstractGTest { TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); td = (TypeDef) dataMgr.resolve(td, null); - struct.setFlexibleArrayComponent(td, "flex", "FlexComment"); + DataTypeComponent flexDtc = struct.add(new ArrayDataType(td, 0, -1), "flex", "FlexComment"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + @@ -1140,15 +1214,17 @@ public class StructureDBTest extends AbstractGTest { " 0 byte 1 field1 \"Comment1\"\n" + " 2 word 2 null \"Comment2\"\n" + " 4 byte 1 field4 \"Comment4\"\n" + - " Foo[0] 0 flex \"FlexComment\"\n" + + " 8 Foo[0] 0 flex \"FlexComment\"\n" + "}\n" + "Size = 8 Actual Alignment = 4", struct); //@formatter:on - dataMgr.replaceDataType(struct.getFlexibleArrayComponent().getDataType(), - ByteDataType.dataType, false); + dataMgr.replaceDataType(td, ByteDataType.dataType, false); - assertNotNull(struct.getFlexibleArrayComponent()); + flexDtc = struct.getComponent(flexDtc.getOrdinal()); + assertNotNull(flexDtc); + + // FIXME: repack was not triggered with replacement //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + @@ -1157,7 +1233,7 @@ public class StructureDBTest extends AbstractGTest { " 0 byte 1 field1 \"Comment1\"\n" + " 2 word 2 null \"Comment2\"\n" + " 4 byte 1 field4 \"Comment4\"\n" + - " byte[0] 0 flex \"FlexComment\"\n" + + " 5 byte[0] 0 flex \"FlexComment\"\n" + "}\n" + "Size = 6 Actual Alignment = 2", struct); //@formatter:on @@ -1210,7 +1286,30 @@ public class StructureDBTest extends AbstractGTest { } @Test - public void testReplaceWith() throws InvalidDataTypeException { + public void testReplaceWith() { + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumComponents()); + + Structure newStruct = createStructure("Replaced", 8); + newStruct.setDescription("testReplaceWith()"); + DataTypeComponent dtc0 = newStruct.insert(2, new ByteDataType(), 1, "field3", "Comment1"); + DataTypeComponent dtc1 = newStruct.insert(5, new WordDataType(), 2, null, "Comment2"); + DataTypeComponent dtc2 = newStruct.insert(7, new DWordDataType(), 4, "field8", null); + + struct.replaceWith(newStruct); + assertEquals(15, struct.getLength()); + assertEquals(11, struct.getNumComponents()); + DataTypeComponent[] dtcs = struct.getDefinedComponents(); + assertEquals(3, dtcs.length); + assertEquals(dtc0, dtcs[0]); + assertEquals(dtc1, dtcs[1]); + assertEquals(dtc2, dtcs[2]); + assertEquals("Test", struct.getName()); + assertEquals("", struct.getDescription()); + } + + @Test + public void testReplaceWith2() throws InvalidDataTypeException { // NOTE: non-packed bitfields should remain unchanged when // transitioning endianess even though it makes little sense. @@ -1224,7 +1323,7 @@ public class StructureDBTest extends AbstractGTest { struct.insertBitFieldAt(9, 2, 7, td, 2, "MyBit3", "bitComment3"); struct.growStructure(1); - struct.setFlexibleArrayComponent(td, "myFlex", "flexComment"); + struct.add(new ArrayDataType(td, 0, -1), "myFlex", "flexComment"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Test\n" + @@ -1239,7 +1338,7 @@ public class StructureDBTest extends AbstractGTest { " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + // " 11 undefined 1 null \"\"\n" + - " Foo[0] 0 myFlex \"flexComment\"\n" + + " 12 Foo[0] 0 myFlex \"flexComment\"\n" + "}\n" + "Size = 12 Actual Alignment = 1", struct); //@formatter:on @@ -1263,7 +1362,7 @@ public class StructureDBTest extends AbstractGTest { " 9 Foo:3(4) 1 MyBit2 \"bitComment2\"\n" + " 9 Foo:2(7) 2 MyBit3 \"bitComment3\"\n" + // " 11 undefined 1 null \"\"\n" + - " Foo[0] 0 myFlex \"flexComment\"\n" + + " 12 Foo[0] 0 myFlex \"flexComment\"\n" + "}\n" + "Size = 12 Actual Alignment = 1", newStruct); //@formatter:on @@ -1530,6 +1629,46 @@ public class StructureDBTest extends AbstractGTest { assertEquals(5, comps[1].getOffset()); } + @Test + public void testClearAtOffset() { + + assertEquals(8, struct.getLength()); + + Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1); + struct.insertAtOffset(1, zeroArray, -1); + + assertEquals(8, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[1].getDataType())); + + assertEquals(1, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + assertEquals(3, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertEquals(DWordDataType.class, comps[3].getDataType().getClass()); + + struct.clearAtOffset(1); + + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumComponents()); // 2 undefined components replaced word + comps = struct.getDefinedComponents(); + + assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); + assertEquals(3, comps[1].getOffset()); + assertEquals(3, comps[1].getOrdinal()); + } + @Test public void testDeleteAtOffset() { struct.deleteAtOffset(2); @@ -1540,6 +1679,41 @@ public class StructureDBTest extends AbstractGTest { assertEquals(1, comps[1].getOffset()); } + @Test + public void testDeleteAtOffset2() { + + assertEquals(8, struct.getLength()); + + Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1); + struct.insertAtOffset(1, zeroArray, -1); + + assertEquals(8, struct.getLength()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(5, comps.length); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertEquals(ByteDataType.class, comps[0].getDataType().getClass()); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[1].getDataType())); + + assertEquals(1, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertEquals(WordDataType.class, comps[2].getDataType().getClass()); + + struct.deleteAtOffset(1); + + assertEquals(6, struct.getLength()); + assertEquals(3, struct.getNumComponents()); + comps = struct.getDefinedComponents(); + + assertEquals(DWordDataType.class, comps[1].getDataType().getClass()); + assertEquals(1, comps[1].getOffset()); + } + @Test public void testDeleteComponent() { Structure s = new StructureDataType("test1", 0); @@ -1580,13 +1754,56 @@ public class StructureDBTest extends AbstractGTest { } @Test - public void testGetComponentAt() { - DataTypeComponent dtc = struct.getComponentAt(4); + public void testGetComponentContaining() { + DataTypeComponent dtc = struct.getComponentContaining(4); assertEquals(DWordDataType.class, dtc.getDataType().getClass()); assertEquals(2, dtc.getOrdinal()); assertEquals(3, dtc.getOffset()); } + @Test + public void testGetComponentsContaining() { + List components = struct.getComponentsContaining(4); + assertEquals("[ 2 3 dword 4 field3 null]", components.toString()); + + 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); + + // force components to align + struct.setPackingEnabled(true); + struct.setPackingEnabled(false); + + assertEquals(16, struct.getLength()); + + /** + * /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 + */ + + // DatatypeComponent.toString: + components = struct.getComponentsContaining(9); + assertEquals("[ 5 9 char[0] 0 zarray1 null, 6 9 undefined 1 null null]", + components.toString()); + + components = struct.getComponentsContaining(10); + assertEquals("[ 7 10 undefined 1 null null]", components.toString()); + + components = struct.getComponentsContaining(16); + assertEquals("[ 10 16 long[0] 0 zarray2 null]", components.toString()); + + } + @Test public void testGetDataTypeAt() { Structure s1 = createStructure("Test1", 0); @@ -1594,7 +1811,7 @@ public class StructureDBTest extends AbstractGTest { s1.add(struct); s1.add(new ByteDataType()); - DataTypeComponent dtc = s1.getComponentAt(7); + DataTypeComponent dtc = s1.getComponentContaining(7); assertEquals(struct, dtc.getDataType()); dtc = s1.getDataTypeAt(7); assertEquals(DWordDataType.class, dtc.getDataType().getClass()); @@ -1607,7 +1824,7 @@ public class StructureDBTest extends AbstractGTest { s1.add(new StringDataType(), 10); s1.add(new StringDataType(), 15); - DataTypeComponent dtc = s1.getComponentAt(5); + DataTypeComponent dtc = s1.getComponentContaining(5); DataType dt = dtc.getDataType(); assertEquals(-1, dt.getLength()); assertEquals(10, dtc.getLength()); @@ -1616,7 +1833,10 @@ public class StructureDBTest extends AbstractGTest { } @Test - public void testReplaceVarLengthDataTypes() { + public void testReplaceAtVarLengthDataTypes() { + + // TODO: these tests are too simple since they only replace undefined components + Structure s1 = new StructureDataType("Test1", 25); s1.replaceAtOffset(0, new StringDataType(), 5, null, null); @@ -1626,7 +1846,7 @@ public class StructureDBTest extends AbstractGTest { dataMgr.getRootCategory(); s1 = (Structure) dataMgr.resolve(s1, null); - DataTypeComponent dtc = s1.getComponentAt(5); + DataTypeComponent dtc = s1.getComponentContaining(5); DataType dt = dtc.getDataType(); assertEquals(-1, dt.getLength()); assertEquals(10, dtc.getLength()); @@ -1634,7 +1854,9 @@ public class StructureDBTest extends AbstractGTest { } @Test - public void testReplaceVarLengthDataTypes2() { + public void testReplaceAtVarLengthDataTypes2() { + + // TODO: these tests are too simple since they only replace undefined components Structure s1 = new StructureDataType("Test1", 0x60); s1.replaceAtOffset(0, new StringDataType(), 0xd, null, null); @@ -1650,17 +1872,153 @@ public class StructureDBTest extends AbstractGTest { dataMgr.getRootCategory(); s1 = (Structure) dataMgr.resolve(s1, null); - DataTypeComponent dtc = s1.getComponentAt(0); + DataTypeComponent dtc = s1.getComponentContaining(0); DataType dt = dtc.getDataType(); assertEquals("string", dt.getDisplayName()); assertEquals(0xd, dtc.getLength()); - dtc = s1.getComponentAt(0x31); + dtc = s1.getComponentContaining(0x31); dt = dtc.getDataType(); assertEquals("string", dt.getDisplayName()); assertEquals(0xd, dtc.getLength()); } + @Test + public void testReplaceAtPacked() { + + struct.setPackingEnabled(true); // test case where there is no component + + assertEquals(12, struct.getLength()); + assertEquals(4, struct.getNumDefinedComponents()); + + struct.replaceAtOffset(0, DataType.DEFAULT, -1, "a", null); + struct.replaceAtOffset(1, ByteDataType.dataType, -1, "b", null); + struct.replaceAtOffset(2, ByteDataType.dataType, -1, "c", null); + + assertEquals(12, struct.getLength()); + assertEquals(5, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertTrue(Undefined1DataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(2, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[2].getDataType())); + + assertEquals(4, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertTrue(DWordDataType.dataType.isEquivalent(comps[3].getDataType())); + + assertEquals(8, comps[4].getOffset()); + assertEquals(4, comps[4].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); + + } + + @Test + public void testReplaceAt() { + + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumDefinedComponents()); + + struct.replaceAtOffset(0, DataType.DEFAULT, -1, "a", null); + struct.replaceAtOffset(1, ByteDataType.dataType, -1, "b", null); + struct.replaceAtOffset(2, ByteDataType.dataType, -1, "c", null); + struct.replaceAtOffset(4, CharDataType.dataType, -1, "d", null); + + assertEquals(8, struct.getLength()); + assertEquals(4, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(1, comps[0].getOffset()); + assertEquals(1, comps[0].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(2, comps[1].getOffset()); + assertEquals(2, comps[1].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(4, comps[2].getOffset()); + assertEquals(4, comps[2].getOrdinal()); + assertTrue(CharDataType.dataType.isEquivalent(comps[2].getDataType())); + + assertEquals(7, comps[3].getOffset()); + assertEquals(7, comps[3].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[3].getDataType())); + + } + + @Test + public void testReplaceAtWithZeroLength() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + struct.insertAtOffset(3, zeroArray, -1); + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumDefinedComponents()); + + // replace dword with short + struct.replaceAtOffset(3, ShortDataType.dataType, -1, "b", null); + + assertEquals(8, struct.getLength()); + assertEquals(5, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(WordDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(3, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[2].getDataType())); + + assertEquals(3, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertTrue(ShortDataType.dataType.isEquivalent(comps[3].getDataType())); + + assertEquals(7, comps[4].getOffset()); + assertEquals(6, comps[4].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); + + Array zeroArray2 = new ArrayDataType(CharDataType.dataType, 0, -1); + + // replace float[0] with char[0] + struct.replaceAtOffset(3, zeroArray2, -1, "a", null); + + comps = struct.getDefinedComponents(); + + assertEquals(0, comps[0].getOffset()); + assertEquals(0, comps[0].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[0].getDataType())); + + assertEquals(1, comps[1].getOffset()); + assertEquals(1, comps[1].getOrdinal()); + assertTrue(WordDataType.dataType.isEquivalent(comps[1].getDataType())); + + assertEquals(3, comps[2].getOffset()); + assertEquals(2, comps[2].getOrdinal()); + assertTrue(zeroArray.isEquivalent(comps[2].getDataType())); + + assertEquals(3, comps[3].getOffset()); + assertEquals(3, comps[3].getOrdinal()); + assertTrue(zeroArray2.isEquivalent(comps[3].getDataType())); + + assertEquals(7, comps[4].getOffset()); + assertEquals(8, comps[4].getOrdinal()); + assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); + } + @Test public void testSetName() throws Exception { Structure s1 = new StructureDataType("Test1", 0); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java index d63611dc97..2dce5112dc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * - */ package ghidra.program.database.data; import static org.junit.Assert.*; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressFactoryTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressFactoryTest.java index 0f132534c6..b18b4b57d5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressFactoryTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressFactoryTest.java @@ -15,8 +15,7 @@ */ package ghidra.program.model.address; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.*; diff --git a/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java b/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java index afcf743da7..8ec2721627 100644 --- a/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java +++ b/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java @@ -84,8 +84,8 @@ public class Pic24DInitAnalyzer extends AbstractAnalyzer { dataRecordType.addBitField(LongDataType.dataType, 9, "page", null); // TODO: factor into dst ram reference } catch (InvalidDataTypeException e) { throw new AssertException(e); - } - dataRecordType.setFlexibleArrayComponent(ByteDataType.dataType, "data", null); + } + dataRecordType.add(new ArrayDataType(ByteDataType.dataType, 0, -1), "data", null); dataRecordType = (Structure) program.getDataTypeManager().resolve(dataRecordType, null); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java index 7b460868be..0f1b09d39c 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java @@ -306,7 +306,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { struct.add(new ByteDataType(), "myByteElement2", "another non-packed byte"); struct.add(new DWordDataType(), "myDWordElement", "non-packed dword"); if (includeFlexArray) { - struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "flex", "unsized flexible array"); } struct.clearComponent(1); @@ -341,7 +341,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { struct.add(new ByteDataType(), "myByteElement2", "alignment 1"); struct.add(new DWordDataType(), "myDWordElement", "alignment 4"); if (includeBitFieldsAndFlexArray) { - struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", + struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "flex", "unsized flexible array"); } struct.clearComponent(1); 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 new file mode 100644 index 0000000000..ff7acef630 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test/java/ghidra/program/database/data/DataDBTest.java @@ -0,0 +1,181 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.database.data; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGenericTest; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; + +public class DataDBTest extends AbstractGenericTest { + + private Program program; + private Listing listing; + + @Before + public void setUp() throws Exception { + program = buildProgram(); + listing = program.getListing(); + program.startTransaction("TEST"); + + /** + * /structA + * pack(disabled) + * Structure structA { + * 0 int 4 a0 "" + * 4 char[0] 0 az "" + * 5 byte 1 a1 "" + * 6 char[0] 0 aflex "" + * } + * Size = 6 Actual Alignment = 1 + */ + Structure structA = new StructureDataType("structA", 0); + structA.add(IntegerDataType.dataType, "a0", null); + structA.add(new ArrayDataType(CharDataType.dataType, 0, -1), "az", null); + structA.add(DataType.DEFAULT); + structA.add(ByteDataType.dataType, "a1", null); + structA.add(new ArrayDataType(IntegerDataType.dataType, 0, -1), "aflex", null); + + /** + * /structB + * pack() + * Structure structB { + * 0 short 2 b0 "" + * 4 int:0(0) 0 "" + * 4 byte[0] 0 bfs "" + * 4 int:4(0) 1 bf1 "" + * 4 int:6(4) 2 bf2 "" + * 5 int:2(2) 1 bf3 "" + * 6 structA 6 b1 "" + * } + * Size = 12 Actual Alignment = 4 + */ + Structure structB = new StructureDataType("structB", 0); + structB.setPackingEnabled(true); + structB.add(ShortDataType.dataType, "b0", null); + structB.addBitField(IntegerDataType.dataType, 0, null, null); // force integer alignment + structB.add(new ArrayDataType(ByteDataType.dataType, 0, -1), "bfs", null); + structB.addBitField(IntegerDataType.dataType, 4, "bf1", null); + structB.addBitField(IntegerDataType.dataType, 6, "bf2", null); + structB.addBitField(IntegerDataType.dataType, 2, "bf3", null); + structB.add(structA, "b1", null); + + /** + * /structC + * pack() + * Structure structC { + * 0 char 1 c0 "" + * 4 structB 12 c1 "" + * } + * Size = 16 Actual Alignment = 4 + */ + Structure structC = new StructureDataType("structC", 0); + structC.setPackingEnabled(true); + structC.add(CharDataType.dataType, "c0", null); + structC.add(structB, "c1", null); + + ProgramBasedDataTypeManager dtm = program.getDataTypeManager(); + structC = (Structure) dtm.resolve(structC, null); + + listing.createData(addr(0x1100), structC); + + } + + private Address addr(long offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } + + private Program buildProgram() throws Exception { + ProgramBuilder builder = new ProgramBuilder("Test", ProgramBuilder._TOY); + builder.createMemory("test1", "0x1000", 0x200); + return builder.getProgram(); + } + + @Test + public void testGetComponentFromPath() throws Exception { + + Data d = listing.getDataAt(addr(0x1100)); + Data c = d.getComponent(new int[] { 1 }); + assertNotNull(c); + assertEquals("c1", c.getComponentPathName()); + + c = d.getComponent(new int[] { 1, 6 }); + assertNotNull(c); + assertEquals("c1.b1", c.getComponentPathName()); + + c = d.getComponent(new int[] { 1, 6, 3 }); + assertNotNull(c); + assertEquals("c1.b1.a1", c.getComponentPathName()); + + c = d.getComponent(new int[] { 1, 6, 4 }); // may not be visible in listing display + assertNotNull(c); + assertEquals("c1.b1.aflex", c.getComponentPathName()); + + } + + @Test + public void testGetComponentContainingAt() throws Exception { + + Data d = listing.getDataAt(addr(0x1100)); + Data c = d.getComponentContaining(4); + assertNotNull(c); + assertEquals("c1", c.getComponentPathName()); + + Data c2 = c.getComponentContaining(4); + assertNotNull(c2); + assertEquals("c1.", c2.getComponentPathName()); // zero-bitfield has no name + + Data c3 = c.getComponentContaining(5); + assertNotNull(c3); + assertEquals("c1.bf2", c3.getComponentPathName()); + + } + + @Test + public void testGetComponentsContaining() throws Exception { + + Data d = listing.getDataAt(addr(0x1100)); + Data c = d.getComponent(new int[] { 1 }); + assertNotNull(c); + assertEquals("c1", c.getComponentPathName()); + + List dataComponents = c.getComponentsContaining(4); + assertEquals("[int:0 , db[0] , int:4 0h, int:6 0h]", dataComponents.toString()); + + c = d.getComponent(new int[] { 1, 6 }); + assertNotNull(c); + assertEquals("c1.b1", c.getComponentPathName()); + + dataComponents = c.getComponentsContaining(4); + assertEquals("[char[0] , ?? 00h]", dataComponents.toString()); + + dataComponents = c.getComponentsContaining(5); + assertEquals("[db 0h]", dataComponents.toString()); + + // last structA.aflex component is beyond data bounds + dataComponents = c.getComponentsContaining(6); + assertNull(dataComponents); + } + +}