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 01caf5eff8..63007cc10c 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 @@ -82,6 +82,7 @@ public class DataTypeMergeManager implements MergeResolver { private LongObjectHashtable latestResolvedDts; // maps Latest data type key -> resolved Data type private LongObjectHashtable origResolvedDts; // maps Original data type key -> resolved Data type private List fixUpList; // FixUpInfo objects that must be resolved after + private HashSet fixUpIDSet; // track types with fixups // data types have been added and conflicts resolved. private LongObjectHashtable cleanupPlaceHolderList; // placeholders that need to be removed. private int progressIndex; // index for showing progress @@ -509,7 +510,7 @@ public class DataTypeMergeManager implements MergeResolver { sb.append(" Data type name " + dt.getName() + ", component " + compDt.getDisplayName() + "\n"); } - showMessage("Unresolved Data Types", sb.toString()); + showMessage("Unresolved Data Types and Components", sb.toString()); } } @@ -1080,9 +1081,86 @@ public class DataTypeMergeManager implements MergeResolver { return resolvedDt; } + private void removeFixUps(long sourceDtID) { + if (!fixUpIDSet.remove(sourceDtID)) { + return; + } + Iterator iter = fixUpList.iterator(); + while (iter.hasNext()) { + FixUpInfo info = iter.next(); + if (info.id == sourceDtID) { + iter.remove(); + } + } + } + + private void updateFlexArray(long sourceDtID, Structure sourceDt, Structure destStruct, + LongObjectHashtable 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); + } + } + try { + if (resultCompDt != null) { + // There is a matching component data type in the result. + try { + // If I have compDt, it should now be from result DTM. + 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); + } + } + else { + destStruct.clearFlexibleArrayComponent(); + } + } + catch (IllegalArgumentException e) { + displayError(destStruct, e); + } + } + private void updateStructure(long sourceDtID, Structure sourceDt, Structure destStruct, LongObjectHashtable resolvedDataTypes) { + // NOTE: it is possible for the same destStruct to be updated more than once; + // therefor we must cleanup any previous obsolete fixups + removeFixUps(sourceDtID); + // Get an empty destination structure that is the correct size. destStruct.deleteAll(); @@ -1091,21 +1169,36 @@ public class DataTypeMergeManager implements MergeResolver { DataTypeManager sourceDTM = sourceDt.getDataTypeManager(); boolean aligned = sourceDt.isInternallyAligned(); - if (!aligned) { - destStruct.growStructure(sourceDt.getLength()); - } // Add each of the defined components back in. DataTypeComponent[] comps = sourceDt.getDefinedComponents(); - for (DataTypeComponent comp : comps) { - DataType dt = comp.getDataType(); - String comment = comp.getComment(); + int lastOffset = 0; + if (comps.length != 0) { + lastOffset = comps[comps.length - 1].getOffset(); + } + for (DataTypeComponent sourceComp : comps) { + DataType sourceCompDt = sourceComp.getDataType(); + BitFieldDataType bfDt = null; + String comment = sourceComp.getComment(); DataType resultCompDt = null; - long sourceComponentID = sourceDTM.getID(dt); + + if (sourceComp.isBitFieldComponent()) { + // NOTE: primitive type will be used if unable to resolve base type + bfDt = (BitFieldDataType) sourceCompDt; + sourceCompDt = bfDt.getBaseDataType(); + if (sourceCompDt instanceof AbstractIntegerDataType) { + resultCompDt = sourceCompDt.clone(dtms[RESULT]); + } + } + + long sourceComponentID = sourceDTM.getID(sourceCompDt); boolean deletedInLatest = false; // Try to get a mapping of the source data type to a result data type. - resultCompDt = getResolvedComponent(sourceComponentID, resolvedDataTypes); + if (resultCompDt == null) { + resultCompDt = getResolvedComponent(sourceComponentID, resolvedDataTypes); + } + if (resultCompDt == null) { // We didn't have a map entry for the data type. @@ -1120,74 +1213,142 @@ public class DataTypeMergeManager implements MergeResolver { // must have been deleted in LATEST // put an entry in the fixup list if this is a conflict deletedInLatest = true; - FixUpInfo info = new FixUpInfo(sourceDtID, sourceComponentID, - sourceDt.isInternallyAligned() ? comp.getOrdinal() : comp.getOffset(), - resolvedDataTypes); - fixUpList.add(info); } } - else { + if (resultCompDt == null) { // Not added/resolved yet // put an entry in the fixup list - fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID, - sourceDt.isInternallyAligned() ? comp.getOrdinal() : comp.getOffset(), + fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID, sourceComp, resolvedDataTypes)); + fixUpIDSet.add(sourceDtID); + } + if (bfDt != null && + (resultCompDt == null || !BitFieldDataType.isValidBaseDataType(resultCompDt))) { + // use primitive type as fallback (may get fixed-up later) + resultCompDt = bfDt.getPrimitiveBaseDataType(); } } try { if (resultCompDt != null) { + + int length = resultCompDt.getLength(); + if (length <= 0) { + length = sourceComp.getLength(); + } + // There is a matching component data type in the result. if (aligned) { - try { - // If I have compDt, it should now be from result DTM. - destStruct.add(resultCompDt, comp.getLength(), comp.getFieldName(), - comment); + if (bfDt != null) { + destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(), + sourceComp.getFieldName(), comment); } - catch (IllegalArgumentException e) { - displayError(destStruct, e); - DataType badDt = BadDataType.dataType; - comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " + - e.getMessage() + " " + ((comment != null) ? (" " + comment) : ""); - destStruct.add(badDt, comp.getLength(), comp.getFieldName(), comment); + else { + try { + // If I have compDt, it should now be from result DTM. + destStruct.add(resultCompDt, length, sourceComp.getFieldName(), + comment); + } + catch (IllegalArgumentException e) { + displayError(destStruct, e); + DataType badDt = BadDataType.dataType; + comment = "Couldn't add " + resultCompDt.getDisplayName() + + " here. " + e.getMessage() + " " + + ((comment != null) ? (" " + comment) : ""); + destStruct.add(badDt, sourceComp.getLength(), + sourceComp.getFieldName(), comment); + } } } + else if (bfDt != null) { + destStruct.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(), + bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(), + sourceComp.getFieldName(), comment); + } else { try { // If I have compDt, it should now be from result DTM. - destStruct.replaceAtOffset(comp.getOffset(), resultCompDt, - comp.getLength(), comp.getFieldName(), comment); + // If not last component must constrain length to original component size + int offset = sourceComp.getOffset(); + if (offset < lastOffset && length > sourceComp.getLength()) { + // The data type is too big, so adjust the component length to what will fit. + int extraBytesNeeded = length - sourceComp.getLength(); + length = sourceComp.getLength(); + // Output a warning indicating the structure has a data type that doesn't fit. + String message = + "Structure Merge: Not enough undefined bytes to fit " + + resultCompDt.getPathName() + " in structure " + + destStruct.getPathName() + " at offset 0x" + + Integer.toHexString(offset) + "." + "\nIt needs " + + extraBytesNeeded + " more byte(s) to be able to fit."; + Msg.warn(this, message); + } + destStruct.insertAtOffset(sourceComp.getOffset(), resultCompDt, length, + sourceComp.getFieldName(), comment); } catch (IllegalArgumentException e) { displayError(destStruct, e); DataType badDt = BadDataType.dataType; comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " + e.getMessage() + " " + ((comment != null) ? (" " + comment) : ""); - destStruct.replaceAtOffset(comp.getOffset(), badDt, comp.getLength(), - comp.getFieldName(), comment); + destStruct.insertAtOffset(sourceComp.getOffset(), badDt, + sourceComp.getLength(), sourceComp.getFieldName(), comment); } } } else if (aligned) { // Add a Bad data type to prevent the ordinal values and component count from changing. // These should get cleaned up later in the conflict cleanup code. - destStruct.add(BadDataType.dataType, comp.getLength(), comp.getFieldName(), - comment); + destStruct.add(BadDataType.dataType, sourceComp.getLength(), + sourceComp.getFieldName(), comment); } else if (!deletedInLatest) { // If the data type wasn't removed and isn't the result, // put a Bad data type to try to keep field name and comment. // If it was deleted, there should already be default data types in place of this component. - destStruct.replaceAtOffset(comp.getOffset(), BadDataType.dataType, - comp.getLength(), comp.getFieldName(), comment); + destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType, + sourceComp.getLength(), sourceComp.getFieldName(), comment); } + } - catch (IllegalArgumentException e) { + catch (IllegalArgumentException | InvalidDataTypeException e) { displayError(destStruct, e); } } + if (!aligned) { + adjustStructureSize(destStruct, sourceDt.getLength()); + } + + updateFlexArray(sourceDtID, sourceDt, destStruct, resolvedDataTypes); } - private void displayError(Composite destComposite, IllegalArgumentException e) { + /** + * Bitfield insertions can cause excess growth of structure which must be trimmed. + * @param struct structure to be trimmed + * @param preferredSize preferred structure size + */ + private static void adjustStructureSize(Structure struct, int preferredSize) { + + DataTypeComponent dtc = struct.getComponentAt(preferredSize); + if (dtc == null) { + struct.growStructure(preferredSize - struct.getLength()); + return; + } + + int startOrdinal = dtc.getOrdinal(); + if (dtc.getOffset() != preferredSize) { + ++startOrdinal; + } + + for (int i = struct.getNumComponents() - 1; i >= startOrdinal; i--) { + DataTypeComponent comp = struct.getComponent(i); + if (comp.getOffset() < preferredSize || comp.getDataType() != DataType.DEFAULT) { + break; + } + struct.delete(i); + } + } + + private void displayError(Composite destComposite, Exception e) { String msg = "Some of your changes to " + destComposite.getName() + " cannot be merged.\nProblem: " + e.getMessage(); String typeName = (destComposite instanceof Union) ? "Union" : "Structure"; @@ -1197,6 +1358,10 @@ public class DataTypeMergeManager implements MergeResolver { private void updateUnion(long sourceDtID, Union sourceDt, Union destUnion, LongObjectHashtable resolvedDataTypes) { + // NOTE: it is possible for the same destUnion to be updated more than once; + // therefor we must cleanup any previous obsolete fixups + removeFixUps(sourceDtID); + // Remove all the components from the destination union. while (destUnion.getNumComponents() > 0) { destUnion.delete(0); @@ -1208,12 +1373,27 @@ public class DataTypeMergeManager implements MergeResolver { DataTypeManager sourceDTM = sourceDt.getDataTypeManager(); // Add each of the defined components back in. - for (int i = 0; i < sourceDt.getNumComponents(); i++) { - DataTypeComponent sourceComp = sourceDt.getComponent(i); - String comment = sourceComp.getComment(); + for (DataTypeComponent sourceComp : sourceDt.getComponents()) { DataType sourceCompDt = sourceComp.getDataType(); + BitFieldDataType bfDt = null; + String comment = sourceComp.getComment(); + DataType resultCompDt = null; + + if (sourceComp.isBitFieldComponent()) { + // NOTE: primitive type will be used if unable to resolve base type + bfDt = (BitFieldDataType) sourceCompDt; + sourceCompDt = bfDt.getBaseDataType(); + if (sourceCompDt instanceof AbstractIntegerDataType) { + resultCompDt = sourceCompDt.clone(dtms[RESULT]); + } + } + long sourceCompID = sourceDTM.getID(sourceCompDt); - DataType resultCompDt = getResolvedComponent(sourceCompID, resolvedDataTypes); + + // Try to get a mapping of the source data type to a result data type. + if (resultCompDt == null) { + resultCompDt = getResolvedComponent(sourceCompID, resolvedDataTypes); + } if (resultCompDt == null) { if (!myDtAddedList.contains(Long.valueOf(sourceCompID))) { @@ -1228,51 +1408,57 @@ public class DataTypeMergeManager implements MergeResolver { // put an entry in RESULT for later fixup if // it is in conflict FixUpInfo info = - new FixUpInfo(sourceDtID, sourceCompID, i, resolvedDataTypes); + new FixUpInfo(sourceDtID, sourceCompID, sourceComp, resolvedDataTypes); fixUpList.add(info); + fixUpIDSet.add(sourceDtID); } } else { // Not added/resolved yet // put an entry in RESULT for later fixup - fixUpList.add(new FixUpInfo(sourceDtID, sourceCompID, i, resolvedDataTypes)); + fixUpList.add( + new FixUpInfo(sourceDtID, sourceCompID, sourceComp, resolvedDataTypes)); + fixUpIDSet.add(sourceDtID); + } + if (bfDt != null && + (resultCompDt == null || !BitFieldDataType.isValidBaseDataType(resultCompDt))) { + // use primitive type as fallback (may get fixed-up later) + resultCompDt = bfDt.getPrimitiveBaseDataType(); } } - if (resultCompDt != null) { - // There is a matching component data type in the result. - DataTypeComponent resultComp; - try { - try { - int resultCompDtLength = resultCompDt.getLength(); - if (resultCompDtLength > 0) { - resultComp = destUnion.add(resultCompDt); + try { + if (resultCompDt != null) { + if (bfDt != null) { + destUnion.addBitField(resultCompDt, bfDt.getBitSize(), + sourceComp.getFieldName(), comment); + } + else { + // There is a matching component data type in the result. + int compLen = resultCompDt.getLength(); + if (compLen <= 0) { + compLen = sourceComp.getLength(); } - else { - resultComp = destUnion.add(resultCompDt, sourceComp.getLength()); + try { + destUnion.add(resultCompDt, compLen, sourceComp.getFieldName(), + comment); + } + catch (IllegalArgumentException e1) { + displayError(destUnion, e1); + DataType badDt = BadDataType.dataType; + comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " + + e1.getMessage() + ((comment != null) ? (" " + comment) : ""); + destUnion.add(badDt, sourceComp.getLength(), sourceComp.getFieldName(), + comment); } } - catch (IllegalArgumentException e1) { - displayError(destUnion, e1); - DataType badDt = BadDataType.dataType; - comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " + - e1.getMessage() + ((comment != null) ? (" " + comment) : ""); - resultComp = destUnion.add(badDt, sourceComp.getLength()); - } - try { - resultComp.setFieldName(sourceComp.getFieldName()); - } - catch (DuplicateNameException e) { - // Since all components were removed originally, shouldn't see this. - } - resultComp.setComment(comment); } - catch (IllegalArgumentException e1) { - displayError(destUnion, e1); + else { + destUnion.add(BadDataType.dataType, sourceComp.getLength(), + sourceComp.getFieldName(), comment); } } - else { - destUnion.add(BadDataType.dataType, sourceComp.getLength(), - sourceComp.getFieldName(), comment); + catch (IllegalArgumentException | InvalidDataTypeException e) { + displayError(destUnion, e); } } } @@ -1309,6 +1495,10 @@ public class DataTypeMergeManager implements MergeResolver { FunctionDefinition sourceFunctionDefDt, FunctionDefinition destDt, LongObjectHashtable resolvedDataTypes) { + // NOTE: it is possible for the same function def to be updated more than once; + // therefor we must cleanup any previous obsolete fixups + removeFixUps(sourceFunctionDefDtID); + long oldLastChangeTime = sourceFunctionDefDt.getLastChangeTime(); long oldLastChangeTimeInSourceArchive = sourceFunctionDefDt.getLastChangeTimeInSourceArchive(); @@ -1344,21 +1534,21 @@ public class DataTypeMergeManager implements MergeResolver { /** * Get the resolved data type for either the return type or a variable. * @param id id of FunctionDefinition - * @param paramID ID of either the return type or a variable + * @param paramDatatypeID ID of either the return or variable dataty type ID * @param index >=0 is the index of the variable; <0 means the paramID is * the return type * @param resolvedDataTypes hashtable to use for resolving * @return resolved data type or the default data type if the data type * has not been resolved yet */ - private DataType getResolvedParam(long id, long paramID, int index, + private DataType getResolvedParam(long id, long paramDatatypeID, int index, LongObjectHashtable resolvedDataTypes) { - DataType resolvedDt = getResolvedComponent(paramID, resolvedDataTypes); + DataType resolvedDt = getResolvedComponent(paramDatatypeID, resolvedDataTypes); if (resolvedDt == null) { - if (!myDtAddedList.contains(Long.valueOf(paramID))) { + if (!myDtAddedList.contains(Long.valueOf(paramDatatypeID))) { // Not added so should be in result if it wasn't deleted there. - DataType resultsDt = dtms[RESULT].getDataType(paramID); + DataType resultsDt = dtms[RESULT].getDataType(paramDatatypeID); if (resultsDt != null) { resolvedDt = resultsDt; } @@ -1367,15 +1557,17 @@ public class DataTypeMergeManager implements MergeResolver { // put an entry in RESULT for later fixup if // it is in conflict resolvedDt = DataType.DEFAULT; - FixUpInfo info = new FixUpInfo(id, paramID, index, resolvedDataTypes); + FixUpInfo info = new FixUpInfo(id, paramDatatypeID, index, resolvedDataTypes); fixUpList.add(info); + fixUpIDSet.add(id); } } else { // Not added/resolved yet // put an entry in RESULT for later fixup resolvedDt = DataType.DEFAULT; - fixUpList.add(new FixUpInfo(id, paramID, index, resolvedDataTypes)); + fixUpList.add(new FixUpInfo(id, paramDatatypeID, index, resolvedDataTypes)); + fixUpIDSet.add(id); } } return resolvedDt; @@ -2058,31 +2250,34 @@ public class DataTypeMergeManager implements MergeResolver { private void fixUpDataTypes() { // fix data types in the fixUpList - ArrayList tempList = new ArrayList<>(fixUpList); - long currentID = -1; - long previousID = -1; + ArrayList unresolvedFixups = new ArrayList<>(); - for (int i = 0; i < tempList.size(); i++) { + for (int i = 0; i < fixUpList.size(); i++) { - FixUpInfo info = tempList.get(i); + FixUpInfo info = fixUpList.get(i); DataType dt = info.ht.get(info.id); - if (dt instanceof Composite) { - previousID = currentID; - currentID = info.id; - - Composite destCdt = (Composite) dt; - - if (dt instanceof Union) { - if (previousID != currentID) { - fixUpUnion(info.id, (Union) destCdt); + if (dt instanceof Union) { + // Fixups for a union are done all at once + // Determine number of applicable fixups (assumes they are sequential) + int count = 1; + for (int n = i + 1; n < fixUpList.size(); n++) { + if (fixUpList.get(n).id != info.id) { + break; } + ++count; } - else { - fixUpStructure(info, (Structure) destCdt); + fixUpUnion(info.id, (Union) dt, i, count, unresolvedFixups); + i += count - 1; + } + else if (dt instanceof Structure) { + if (!fixUpStructure(info, (Structure) dt)) { + unresolvedFixups.add(info); } } else if (dt instanceof FunctionDefinition) { - fixUpFunctionDef(info, (FunctionDefinition) dt); + if (!fixUpFunctionDef(info, (FunctionDefinition) dt)) { + unresolvedFixups.add(info); + } } else { DataTypeManager dtm = info.getDataTypeManager(); @@ -2095,12 +2290,19 @@ public class DataTypeMergeManager implements MergeResolver { // DataType sourceDt = info.getDataTypeManager().getDataType(info.id); // System.out.println("Couldn't fixup " + sourceDt.getPathName()); // } - fixUpList.remove(info); } } + // update fixup list with those that were unresolved + fixUpList = unresolvedFixups; } - private void fixUpFunctionDef(FixUpInfo info, FunctionDefinition fd) { + /** + * Fix up the function definition using the fix up info for a component. + * @param info fixup info + * @param fd function definition to be fixed-up + * @return true if fixup successfully processed else false + */ + private boolean fixUpFunctionDef(FixUpInfo info, FunctionDefinition fd) { long lastChangeTime = fd.getLastChangeTime(); // Don't let the time change. DataType dt = resolve(info.compID, info.getDataTypeManager(), info.ht); @@ -2114,97 +2316,223 @@ public class DataTypeMergeManager implements MergeResolver { } } fd.setLastChangeTime(lastChangeTime); // Reset the last change time to the merged data type's. - fixUpList.remove(info); + return true; } /** - * Fix up the structure using the fix up info for a component. + * Process fixup for aligned structure component or trailing flexible array + * @param info fixup info + * @param struct result structure + * @param dt component datatype + * @return false if component not found, else true */ - private void fixUpStructure(FixUpInfo info, Structure struct) { + private boolean fixUpAlignedStructureComponent(FixUpInfo info, Structure struct, DataType dt) { + int ordinal = info.index; + boolean isFlexArrayFixup = (info.index == Integer.MAX_VALUE); - long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change. - DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht); - - if (compDt != null) { - int length = compDt.getLength(); - if (length == 0) { - length = 1; // Don't want 0 sized components. - } - if (struct.isInternallyAligned()) { - int ordinal = info.index; - int numComponents = struct.getNumComponents(); - if (ordinal >= 0 && ordinal < numComponents) { - DataTypeComponent dtc = struct.getComponent(ordinal); - dtc = struct.replace(ordinal, compDt, length, dtc.getFieldName(), - dtc.getComment()); - } - else { - Msg.warn(this, "Data Type Merge: Couldn't get component " + ordinal + " in " + - struct.getPathName() + " data type during fix up."); - return; // Don't remove this FixUpInfo from the fixupList so the user will get notified. - } - } - else { - int offset = info.index; - DataTypeComponent dtc = struct.getComponentAt(offset); - if (dtc != null) { - int ordinal = dtc.getOrdinal(); - int dtcLength = dtc.getLength(); - if (length < 0) { - length = dtcLength; - } - int bytesNeeded = length - dtcLength; - if (bytesNeeded > 0) { - int bytesAvailable = getNumUndefinedBytes(struct, ordinal + 1); - if (bytesAvailable < bytesNeeded) { - // The data type is too big, so adjust the component length to what will fit. - length = dtcLength + bytesAvailable; - // Output a warning indicating the structure has a data type that doesn't fit. - String message = - "Merging Data Types: Not enough undefined bytes to fit " + - compDt.getPathName() + " in structure " + struct.getPathName() + - " at offset 0x" + Integer.toHexString(offset) + "." + - "\nIt needs " + (bytesNeeded - bytesAvailable) + - " more byte(s) to be able to fit."; - Msg.warn(this, message); - } - } - dtc = struct.replaceAtOffset(offset, compDt, length, dtc.getFieldName(), - dtc.getComment()); - } - else { - Msg.warn(this, "Couldn't get component at offset " + offset + " in " + - struct.getPathName()); - } + DataTypeComponent dtc = null; + if (isFlexArrayFixup) { + dtc = struct.getFlexibleArrayComponent(); + } + else { + if (ordinal >= 0 || ordinal < struct.getNumComponents()) { + dtc = struct.getComponent(ordinal); } } - // Check to see if we have a placeholder - if (struct.isInternallyAligned()) { - int ordinal = info.index; - int numComponents = struct.getNumComponents(); - if (ordinal >= 0 && ordinal < numComponents) { - DataTypeComponent component = struct.getComponent(ordinal); - DataType dataType = component.getDataType(); - // Check to see if we have a placeholder, wait to remove it so we don't mess up ordinals. - if (dataType == BadDataType.dataType) { - addToCleanupList(info); + 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 (BitFieldDataType.isValidBaseDataType(dt)) { + // replace bitfield base datatype - silent if updated type is not a valid base type + BitFieldDataType bfDt = (BitFieldDataType) dtc.getDataType(); + struct.delete(ordinal); + try { + struct.insertBitField(ordinal, bfDt.getLength(), bfDt.getBitOffset(), dt, + bfDt.getDeclaredBitSize(), dtc.getFieldName(), dtc.getComment()); + } + catch (InvalidDataTypeException e) { + Msg.error(this, "Unexpected datatype merge fixup error", e); } } } else { - int offset = info.index; - DataTypeComponent component = struct.getComponentAt(offset); - if (component != null) { - DataType dataType = component.getDataType(); - if (dataType == BadDataType.dataType) { - // Clear the placeholder. - struct.clearComponent(component.getOrdinal()); + // handle non-bitfield component fixup + int length = dt.getLength(); + if (length <= 0) { + length = dtc.getLength(); + } + try { + struct.replace(ordinal, dt, length, dtc.getFieldName(), dtc.getComment()); + } + catch (IllegalArgumentException e) { + displayError(struct, e); + DataType badDt = BadDataType.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); + } + } + return true; + } + + /** + * Process fixup for unaligned structure component + * @param info fixup info + * @param struct result structure + * @param dt component datatype + * @return false if component not found, else true + */ + private boolean fixUpUnalignedStructureComponent(FixUpInfo info, Structure struct, + DataType dt) { + int offset = info.index; + DataTypeComponent dtc = struct.getComponentAt(offset); + if (dtc == null) { + return false; + } + if (dtc.isBitFieldComponent()) { + dtc = info.findStructureBitFieldComponentAtOrAfter(struct, dtc); + if (dtc != null) { + if (BitFieldDataType.isValidBaseDataType(dt)) { + // replace bitfield base datatype - silent if updated type is not a valid base type + BitFieldDataType bfDt = (BitFieldDataType) dtc.getDataType(); + struct.delete(dtc.getOrdinal()); + try { + struct.insertBitFieldAt(dtc.getOffset(), bfDt.getLength(), + bfDt.getBitOffset(), dt, bfDt.getDeclaredBitSize(), dtc.getFieldName(), + dtc.getComment()); + } + catch (InvalidDataTypeException e) { + // should never occur + Msg.error(this, "Unexpected bitfield merge fixup error", e); + } + } + } + else { + Msg.error(this, "Structure Merge: failed to identify bitfield fixup component (\n" + + info + ")"); + } + } + else { + // handle non-bitfield component fixup + int ordinal = dtc.getOrdinal(); + int dtcLength = dtc.getLength(); + int length = dt.getLength(); + if (length <= 0) { + length = dtcLength; + } + int bytesNeeded = length - dtcLength; + if (bytesNeeded > 0) { + int bytesAvailable = getNumUndefinedBytes(struct, ordinal + 1); + if (bytesAvailable < bytesNeeded) { + // The data type is too big, so adjust the component length to what will fit. + length = dtcLength + bytesAvailable; + // Output a warning indicating the structure has a data type that doesn't fit. + String message = "Structure Merge: Not enough undefined bytes to fit " + + dt.getPathName() + " in structure " + struct.getPathName() + + " at offset 0x" + Integer.toHexString(offset) + "." + "\nIt needs " + + (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."; + Msg.warn(this, message); + } + } + try { + struct.replaceAtOffset(offset, dt, length, dtc.getFieldName(), dtc.getComment()); + } + catch (IllegalArgumentException e) { + displayError(struct, e); + DataType badDt = BadDataType.dataType; + String comment = dtc.getComment(); + comment = "Couldn't add " + dt.getDisplayName() + " here. " + e.getMessage() + " " + + ((comment != null) ? (" " + comment) : ""); + struct.replaceAtOffset(offset, badDt, dtc.getLength(), dtc.getFieldName(), comment); + } + } + return true; + } + + /** + * Fix up the structure using the fix up info for a component. + * @param info fixup info + * @param struct structure to be fixed-up + * @return true if fixup successfully processed else false + */ + private boolean fixUpStructure(FixUpInfo info, Structure struct) { + + long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change. + try { + + DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht); + + boolean isFlexArrayFixup = (info.index == Integer.MAX_VALUE); + + if (compDt != null) { + if (struct.isInternallyAligned() || isFlexArrayFixup) { + if (!fixUpAlignedStructureComponent(info, struct, compDt)) { + String msg = + isFlexArrayFixup ? "flex-array component" : ("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. + } + return true; + } + + if (!fixUpUnalignedStructureComponent(info, struct, compDt)) { + Msg.warn(this, "Structure Merge: Couldn't get component at offset " + + info.index + " in " + struct.getPathName()); + return false; } } + + // Datatype failed to resolved - check to see if we have a placeholder + else if (isFlexArrayFixup) { + struct.clearFlexibleArrayComponent(); + } + else if (struct.isInternallyAligned()) { + int ordinal = info.index; + int numComponents = struct.getNumComponents(); + if (ordinal >= 0 && ordinal < numComponents) { + DataTypeComponent component = struct.getComponent(ordinal); + DataType dataType = component.getDataType(); + // Check to see if we have a placeholder, wait to remove it so we don't mess up ordinals. + if (dataType == BadDataType.dataType) { + addToCleanupList(info); + } + } + } + else { + int offset = info.index; + DataTypeComponent component = struct.getComponentAt(offset); + if (component != null) { + DataType dataType = component.getDataType(); + if (dataType == BadDataType.dataType) { + // Clear the placeholder. + struct.clearComponent(component.getOrdinal()); + } + + } + } + return true; + } + finally { + struct.setLastChangeTime(lastChangeTime); // Reset the last change time to the merged data type's. } - struct.setLastChangeTime(lastChangeTime); // Reset the last change time to the merged data type's. - fixUpList.remove(info); } /** @@ -2247,74 +2575,108 @@ public class DataTypeMergeManager implements MergeResolver { cleanUpInfo.add(index, ht); } + private void fixUpUnionComponent(Union union, FixUpInfo info) { + int ordinal = info.index; + + DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht); + if (compDt != null) { + + DataTypeComponent dtc = union.getComponent(ordinal); + if (dtc != null && dtc.isBitFieldComponent()) { + if (BitFieldDataType.isValidBaseDataType(compDt)) { + // replace bitfield base datatype - silent if updated type is not a valid base type + BitFieldDataType bfDt = (BitFieldDataType) dtc.getDataType(); + union.delete(ordinal); + try { + union.insertBitField(ordinal, compDt, bfDt.getDeclaredBitSize(), + dtc.getFieldName(), dtc.getComment()); + } + catch (InvalidDataTypeException e) { + // should never occur + Msg.error(this, "Unexpected datatype merge fixup error", e); + } + } + } + else { + // handle non-bitfield component fixup + int length = compDt.getLength(); + if (length <= 0) { + length = dtc.getLength(); + } + union.delete(ordinal); + try { + union.insert(ordinal, compDt, length, dtc.getFieldName(), dtc.getComment()); + } + catch (IllegalArgumentException e) { + displayError(union, e); + DataType badDt = BadDataType.dataType; + String comment = dtc.getComment(); + comment = "Couldn't add " + compDt.getDisplayName() + " here. " + + e.getMessage() + " " + ((comment != null) ? (" " + comment) : ""); + union.insert(ordinal, badDt, dtc.getLength(), dtc.getFieldName(), comment); + } + } + } + + // Datatype failed to resolved - check to see if we have a placeholder + else { + DataTypeComponent component = union.getComponent(ordinal); + DataType dataType = component.getDataType(); + if (dataType == BadDataType.dataType) { + addToCleanupList(info); + } + } + } + /** * Fix up the Union by going through all of the fix up info objects that * have the given ID. * @param id id of the Union * @param union union that is updated + * @param firstFixupIndex first applicable fixupList entry index + * @param fixupCount total number of fixup entries to be applied + * @param unresolvedFixups list to which unprocessed fixups should be added */ - private void fixUpUnion(long id, Union union) { + private void fixUpUnion(long id, Union union, int firstFixupIndex, int fixupCount, + ArrayList unresolvedFixups) { + + // presence of fixup implies union is not empty long lastChangeTime = union.getLastChangeTime(); // Don't let the time change. - int preFixupLength = union.getLength(); - DataTypeComponent[] dtcs = union.getComponents(); - // Add a freeze length component to keep the size from changing during fixup. - // Otherwise other datatypes will respond to size change and update their change times. - union.add(BadDataType.dataType, preFixupLength); - // Delete all but the freezeLengthComponent - while (union.getNumComponents() > 1) { - union.delete(0); - } - for (int ordinal = 0; ordinal < dtcs.length; ordinal++) { - DataTypeComponent rdtc = null; - FixUpInfo info = findFixUpInfo(id, ordinal); - if (info != null) { - DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht); - if (compDt != null) { - int length = compDt.getLength(); - if (length == 0) { - length = 1; // Don't want 0 sized components. - } - rdtc = union.insert(ordinal, compDt, length); + try { + + int preFixupLength = union.getLength(); + int numComponents = union.getNumComponents(); + + // Add a freeze length component to keep the union size from changing during fixup. + // Otherwise other datatypes will respond to size change and update their change times. + union.add(BadDataType.dataType, preFixupLength); + + // Process all fixups for union + int endIndex = firstFixupIndex + fixupCount; + for (int i = firstFixupIndex; i < endIndex; i++) { + FixUpInfo info = fixUpList.get(i); // assume info applies to union + int ordinal = info.index; + if (ordinal < 0 || ordinal >= numComponents) { + Msg.warn(this, "Union Merge: Couldn't get component " + ordinal + " in " + + union.getPathName() + " data type during fix up."); + unresolvedFixups.add(info); } - // Check to see if we have a placeholder - DataTypeComponent component = union.getComponent(ordinal); - DataType dataType = component.getDataType(); - // Check to see if we have a placeholder, re-insert it so we don't mess up ordinals. - if (dataType == BadDataType.dataType) { - if (rdtc == null) { - rdtc = - union.insert(ordinal, BadDataType.dataType, dtcs[ordinal].getLength()); // Put the placeholder back for now. - } - addToCleanupList(info); + else { + fixUpUnionComponent(union, info); } - fixUpList.remove(info); - } - else { - DataType compDt = dtcs[ordinal].getDataType(); - rdtc = union.insert(ordinal, compDt); } - if (rdtc != null) { - try { - rdtc.setFieldName(dtcs[ordinal].getFieldName()); - } - catch (DuplicateNameException e) { - // Since all components were removed originally, shouldn't see this. - } - rdtc.setComment(dtcs[ordinal].getComment()); + // Remove the freeze length component that is no longer needed. + DataType dataType = union.getComponent(numComponents).getDataType(); + // Check to see if we have a placeholder, wait to remove it so we don't mess up ordinals. + if (dataType == BadDataType.dataType) { + union.delete(numComponents); } } - - // Remove the freeze length component that is no longer needed. - int lastIndex = union.getNumComponents() - 1; - DataType dataType = union.getComponent(lastIndex).getDataType(); - // Check to see if we have a placeholder, wait to remove it so we don't mess up ordinals. - if (dataType == BadDataType.dataType) { - union.delete(lastIndex); + finally { + union.setLastChangeTime(lastChangeTime); // Reset the last change time to the merged data type's. } - - union.setLastChangeTime(lastChangeTime); // Reset the last change time to the merged data type's. } /** @@ -2349,15 +2711,15 @@ public class DataTypeMergeManager implements MergeResolver { return dt; } - private FixUpInfo findFixUpInfo(long id, int index) { - for (int i = 0; i < fixUpList.size(); i++) { - FixUpInfo info = fixUpList.get(i); - if (info.id == id && info.index == index) { - return info; - } - } - return null; - } +// private FixUpInfo findFixUpInfo(long id, int index) { +// for (int i = 0; i < fixUpList.size(); i++) { +// FixUpInfo info = fixUpList.get(i); +// if (info.id == id && info.index == index) { +// return info; +// } +// } +// return null; +// } private void processDataTypeSourceChanged(long id) { if (dataTypeSourceWasChanged(id, dtms[MY])) { @@ -2604,6 +2966,7 @@ public class DataTypeMergeManager implements MergeResolver { origResolvedDts = new LongObjectHashtable<>(); fixUpList = new ArrayList<>(); + fixUpIDSet = new HashSet<>(); totalConflictCount += dtConflictList.size(); cleanupPlaceHolderList = new LongObjectHashtable<>(); @@ -2859,6 +3222,14 @@ public class DataTypeMergeManager implements MergeResolver { return dtms[MY]; } + private static int getComponentFixupIndex(DataTypeComponent dtc) { + Composite composite = (Composite) dtc.getParent(); + if (composite.isInternallyAligned() || (composite instanceof Union)) { + return dtc.getOrdinal(); + } + return dtc.getOffset(); + } + /** * FixUpInfo objects that must be resolved after * data types have been added and conflicts resolved. @@ -2869,13 +3240,18 @@ public class DataTypeMergeManager implements MergeResolver { int index; LongObjectHashtable ht; + // bitfield info + int bitOffset = -1; + int bitSize = -1; + /** * Construct info needed to fix up data types after base types * or components were resolved. * @param id id of data type needed to be fixed up * @param compID id of either component or base type * @param index offset into unaligned structure, or ordinal into union or aligned - * structure; for other data types, offset is not used (specify -1) + * 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, @@ -2886,6 +3262,53 @@ public class DataTypeMergeManager implements MergeResolver { this.ht = resolvedDataTypes; } + /** + * Construct info needed to fix up data types after base types + * or components were resolved. + * @param id id of data type needed to be fixed up + * @param compID datatype id of either component or base type + * @param sourceDtc associated composite datatype component + * @param resolvedDataTypes hashtable used for resolving the data type + */ + FixUpInfo(long id, long compID, DataTypeComponent sourceDtc, + LongObjectHashtable resolvedDataTypes) { + this(id, compID, getComponentFixupIndex(sourceDtc), resolvedDataTypes); + if (sourceDtc.isBitFieldComponent()) { + BitFieldDataType bfDt = (BitFieldDataType) sourceDtc.getDataType(); + bitSize = bfDt.getDeclaredBitSize(); + bitOffset = bfDt.getBitOffset(); + } + } + + /** + * Find unaligned structure bitfield component at or after specified component + * which matches this info's bitfield data. + * @param struct structure + * @param dtc structure component contained within struct + * @return bitfield component which matches info or null + */ + DataTypeComponent findStructureBitFieldComponentAtOrAfter(Structure struct, + DataTypeComponent dtc) { + if (bitOffset < 0) { + return null; + } + int maxOrdinal = struct.getNumComponents(); + while (dtc != null && dtc.getOffset() <= index) { + if (dtc.isBitFieldComponent()) { + BitFieldDataType bfDt = (BitFieldDataType) dtc.getDataType(); + if (bitSize == bfDt.getDeclaredBitSize() && bitOffset == bfDt.getBitOffset()) { + return dtc; + } + } + int nextOrdinal = dtc.getOrdinal() + 1; + if (nextOrdinal > maxOrdinal) { + break; + } + struct.getComponent(nextOrdinal); + } + return null; + } + @Override public String toString() { String htStr = "MY"; @@ -2898,9 +3321,13 @@ public class DataTypeMergeManager implements MergeResolver { htStr = "LATEST/RESULTS"; dtm = dtms[LATEST]; } - return "\n" + "ID = " + Long.toHexString(id) + ",\n" + "dt = " + dtm.getDataType(id) + - ",\n" + "component ID = " + Long.toHexString(compID) + ",\n" + "component dt = " + - dtm.getDataType(compID) + ",\n" + "offset/index = " + index + ",\n" + "ht = " + + String bitInfo = ""; + if (bitOffset >= 0) { + bitInfo = "\nbitOffset=" + bitOffset + ",\nbitSize = " + bitSize + ",\n"; + } + return "\n" + "ID = " + Long.toHexString(id) + ",\ndt = " + dtm.getDataType(id) + + ",\ncomponent ID = " + Long.toHexString(compID) + ",\ncomponent dt = " + + dtm.getDataType(compID) + ",\noffset/index = " + index + ",\n" + bitInfo + "ht = " + htStr + "\n"; } 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 2c90b5aa48..eb2f312c7e 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 @@ -17,6 +17,8 @@ package ghidra.app.merge.datatypes; import static org.junit.Assert.*; +import java.util.concurrent.atomic.AtomicReference; + import org.junit.Assert; import org.junit.Test; @@ -35,38 +37,48 @@ import ghidra.util.task.TaskMonitorAdapter; public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { @Test - public void testCategoryAddRemoveDTAdd() throws Exception { + public void testCategoryAddRemoveDTAdd() throws Exception { + + TypeDef td = new TypedefDataType("BF", IntegerDataType.dataType); + + AtomicReference structRef = new AtomicReference<>(); + mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { + boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); int transactionID = program.startTransaction("test"); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); try { - c.removeCategory("Category5", TaskMonitorAdapter.DUMMY_MONITOR); - Category c5 = c.createCategory("Category5"); - Structure dt = new StructureDataType("Test", 0); - dt.add(new ByteDataType()); - dt.add(new WordDataType()); - dt = (Structure) c5.addDataType(dt, DataTypeConflictHandler.DEFAULT_HANDLER); - dt.add(new QWordDataType()); + Structure struct = + new StructureDataType("Test", 0, program.getDataTypeManager()); + struct.add(new ByteDataType()); + struct.add(new WordDataType()); + 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"); + + structRef.set(struct); + + c.removeCategory("Category5", TaskMonitorAdapter.DUMMY); + Category c5 = c.createCategory("Category5"); + c5.addDataType(struct, DataTypeConflictHandler.DEFAULT_HANDLER); commit = true; } - catch (InvalidNameException e) { - Assert.fail("got InvalidNameException!"); + catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.toString()); } finally { program.endTransaction(transactionID, commit); @@ -79,19 +91,19 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category5")); assertNotNull(c); - assertNotNull(c.getDataType("Test")); + DataType dt = c.getDataType("Test"); + assertNotNull(dt); + assertTrue(structRef.get().isEquivalent(dt)); } @Test - public void testDataTypeAddedInMy() throws Exception { + public void testDataTypeAddedInMy() throws Exception { // A category was added to Category5 in the latest; // in My program, rename Category5 to "My Category5" and add a new data type mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -111,9 +123,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -157,20 +166,19 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeAddedInMy2() throws Exception { + public void testDataTypeAddedInMy2() throws Exception { + + TypeDef td = new TypedefDataType("BF", IntegerDataType.dataType); + + AtomicReference structRef = new AtomicReference<>(); mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -179,15 +187,24 @@ 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_MONITOR); - s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0); - s.add(new QWordDataType()); + c.remove(s, TaskMonitorAdapter.DUMMY); + s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0, dtm); + s.add(new QWordDataType(), "f1", "my f1"); s.add(new FloatDataType()); s.add(new ByteDataType()); + s.insertBitFieldAt(16, 2, 6, td, 2, "bf1", "my bf1"); + s.insertBitFieldAt(16, 2, 4, td, 2, "bf2", "my bf2"); s.add(new WordDataType()); + + structRef.set(s); + c.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER); commit = true; } + catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.toString()); + } finally { program.endTransaction(transactionID, commit); } @@ -199,30 +216,25 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); DataType dt = c.getDataType("IntStruct"); assertNotNull(dt); - assertTrue(dt instanceof Structure); - Structure s = (Structure) dt; - assertTrue(new QWordDataType().isEquivalent(s.getComponent(0).getDataType())); - assertTrue(new FloatDataType().isEquivalent(s.getComponent(1).getDataType())); - assertTrue(new ByteDataType().isEquivalent(s.getComponent(2).getDataType())); - assertTrue(new WordDataType().isEquivalent(s.getComponent(3).getDataType())); + assertTrue(structRef.get().isEquivalent(dt)); + Structure s = (Structure) dt; + assertEquals("my f1", s.getComponent(0).getComment()); + DataTypeComponent dtc = s.getComponentAt(17); + assertEquals(7, dtc.getOrdinal()); + assertEquals("my bf1", dtc.getComment()); } @Test - public void testDataTypeAddedInMy3() throws Exception { + public void testDataTypeAddedInMy3() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -231,7 +243,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_MONITOR); + c.remove(s, TaskMonitorAdapter.DUMMY); s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0); s.add(new QWordDataType()); s.add(new FloatDataType()); @@ -270,15 +282,13 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeAddedInLatest() throws Exception { + public void testDataTypeAddedInLatest() throws Exception { // Add A category to Category5 in the latest, add // add a new data type; // in My program, rename Category5 to "My Category5" mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -303,9 +313,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -341,14 +348,12 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeAddedInLatest2() throws Exception { + public void testDataTypeAddedInLatest2() throws Exception { // A category was added to Category5 in the latest; // in My program, rename Category5 to "My Category5" and add a new data type mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -372,9 +377,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -418,20 +420,15 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeDeletedInMy() throws Exception { + public void testDataTypeDeletedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -443,7 +440,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -461,20 +458,15 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeAddedDeletedInMy() throws Exception { + public void testDataTypeAddedDeletedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -487,7 +479,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { try { DataType dt = dtm.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -505,12 +497,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeDeletedChanged() throws Exception { + public void testDataTypeDeletedChanged() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -529,9 +519,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -543,7 +530,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -561,12 +548,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeDeletedChanged2() throws Exception { + public void testDataTypeDeletedChanged2() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -585,9 +570,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -599,7 +581,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "FloatStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -616,12 +598,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeDeletedChanged3() throws Exception { + public void testDataTypeDeletedChanged3() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -631,7 +611,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -639,9 +619,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -669,12 +646,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeDeletedInLatest() throws Exception { + public void testDataTypeDeletedInLatest() throws Exception { mtf.initialize("notepad2", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -684,7 +659,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -692,9 +667,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -728,12 +700,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeDeletedInBoth() throws Exception { + public void testDataTypeDeletedInBoth() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -745,7 +715,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -753,9 +723,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -767,7 +734,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -785,20 +752,15 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDataTypeRenamedInMy() throws Exception { + public void testDataTypeRenamedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -834,12 +796,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testRenamedBoth() throws Exception { + public void testRenamedBoth() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -865,9 +825,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -902,12 +859,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testRenamedBoth2() throws Exception { + public void testRenamedBoth2() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -933,9 +888,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -971,12 +923,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDeletedInMyRenamedInLatest() throws Exception { + public void testDeletedInMyRenamedInLatest() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1002,9 +952,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1016,7 +963,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1032,12 +979,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDeletedInLatestRenamedInMy() throws Exception { + public void testDeletedInLatestRenamedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1047,7 +992,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { "IntStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1055,9 +1000,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1090,12 +1032,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDeletedInLatestChangedInMy() throws Exception { + public void testDeletedInLatestChangedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1110,7 +1050,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_MONITOR); + parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1118,9 +1058,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1161,12 +1098,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testDeletedInLatestAddedInMy() throws Exception { + public void testDeletedInLatestAddedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1181,7 +1116,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_MONITOR); + parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1189,9 +1124,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1230,12 +1162,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } @Test - public void testCompositeCommentChanged() throws Exception { + public void testCompositeCommentChanged() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1258,9 +1188,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; 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 e8ea873e49..fa41507f5e 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 @@ -21,11 +21,11 @@ import org.junit.Assert; import org.junit.Test; import docking.widgets.OptionDialog; -import ghidra.program.database.ProgramDB; -import ghidra.program.database.ProgramModifierListener; +import ghidra.program.database.*; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.util.exception.DuplicateNameException; +import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitorAdapter; /** @@ -47,7 +47,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); // 2 components should get removed from CoolUnion commit = true; } @@ -119,7 +119,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); // 2 components should get removed from CoolUnion commit = true; } @@ -191,7 +191,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); // 2 components should get removed from CoolUnion commit = true; } @@ -296,10 +296,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { setErrorsExpected(true); - executeMerge(); - DataTypeManager dtm = resultProgram.getDataTypeManager(); + executeMerge(true); - waitForCompletion(); + DataTypeManager dtm = resultProgram.getDataTypeManager(); checkConflictCount(0); @@ -573,7 +572,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -646,7 +645,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); // causes Bar to be marked as changed commit = true; } @@ -720,7 +719,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); // causes Bar to be marked as changed commit = true; } @@ -796,7 +795,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { Structure ms = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "MyStruct"); try { - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); Structure s1 = new StructureDataType( new CategoryPath("/Category1/Category2/Category5"), "s1", 0); s1.add(ms); @@ -884,6 +883,196 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { assertEquals(3, dtcs.length); } + @Test + public void testConflictUpdate5() throws Exception { + + TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "BF", + IntegerDataType.dataType); + + mtf.initialize("notepad2", new OriginalProgramModifierListener() { + + @Override + public void modifyOriginal(ProgramDB program) throws Exception { + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + dtm.addDataType(td, null); + } + finally { + program.endTransaction(transactionID, true); + } + } + + @Override + public void modifyLatest(ProgramDB program) { + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "BF"); + try { + dtm.remove(dt, TaskMonitorAdapter.DUMMY); + } + finally { + program.endTransaction(transactionID, true); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), + "Structure_1"); + Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + try { + s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1"); + s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2"); + foo.add(new FloatDataType()); + } + catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.toString()); + } + finally { + program.endTransaction(transactionID, true); + } + } + }); + + // bitfield silently transitions to int since typedef BF was removed + + executeMerge(); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + Structure s1 = + (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); + assertNotNull(s1); + DataTypeComponent[] dtcs = s1.getComponents(); + assertEquals(7, dtcs.length); + + assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined + assertEquals("bf1", dtcs[3].getFieldName()); + assertEquals("my bf1", dtcs[3].getComment()); + + DataType dt = dtcs[3].getDataType(); + assertTrue(dt instanceof BitFieldDataType); + BitFieldDataType bfDt = (BitFieldDataType) dt; + assertTrue(bfDt.getBaseDataType() instanceof IntegerDataType); + assertEquals(2, bfDt.getDeclaredBitSize()); + assertEquals(6, bfDt.getBitOffset()); + + assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined + assertEquals("bf2", dtcs[4].getFieldName()); + assertEquals("my bf2", dtcs[4].getComment()); + + dt = dtcs[4].getDataType(); + assertTrue(dt instanceof BitFieldDataType); + bfDt = (BitFieldDataType) dt; + assertTrue(bfDt.getBaseDataType() instanceof IntegerDataType); + assertEquals(2, bfDt.getDeclaredBitSize()); + assertEquals(4, bfDt.getBitOffset()); + + Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + // Structure_1 should contain MY Foo + assertEquals(foo, dtcs[5].getDataType()); + + dtcs = foo.getComponents(); + assertEquals(5, dtcs.length); + assertTrue(dtcs[4].getDataType().isEquivalent(new FloatDataType())); + checkConflictCount(0); + } + + @Test + public void testConflictUpdate6() throws Exception { + + TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "BF", + IntegerDataType.dataType); + + mtf.initialize("notepad2", new ProgramModifierListener() { + + @Override + public void modifyLatest(ProgramDB program) { + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + // add new BF not compatible with BitFields + dtm.addDataType( + new StructureDataType(new CategoryPath("/Category1/Category2"), "BF", 0), + null); + } + finally { + program.endTransaction(transactionID, true); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), + "Structure_1"); + Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + try { + s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1"); + s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2"); + foo.add(new FloatDataType()); + } + catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.toString()); + } + finally { + program.endTransaction(transactionID, true); + } + } + }); + + // bitfield silently transitions to BF.conflict since two different BF types were added + + executeMerge(); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + Structure s1 = + (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); + assertNotNull(s1); + DataTypeComponent[] dtcs = s1.getComponents(); + assertEquals(7, dtcs.length); + + assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined + assertEquals("bf1", dtcs[3].getFieldName()); + assertEquals("my bf1", dtcs[3].getComment()); + + DataType dt = dtcs[3].getDataType(); + assertTrue(dt instanceof BitFieldDataType); + BitFieldDataType bfDt = (BitFieldDataType) dt; + DataType bdt = bfDt.getBaseDataType(); + assertEquals("/Category1/Category2/BF.conflict", bdt.getPathName()); + assertTrue(bdt.isEquivalent(td)); + assertEquals(2, bfDt.getDeclaredBitSize()); + assertEquals(6, bfDt.getBitOffset()); + + assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined + assertEquals("bf2", dtcs[4].getFieldName()); + assertEquals("my bf2", dtcs[4].getComment()); + + dt = dtcs[4].getDataType(); + assertTrue(dt instanceof BitFieldDataType); + bfDt = (BitFieldDataType) dt; + bdt = bfDt.getBaseDataType(); + assertEquals("/Category1/Category2/BF.conflict", bdt.getPathName()); + assertTrue(bdt.isEquivalent(td)); + assertEquals(2, bfDt.getDeclaredBitSize()); + assertEquals(4, bfDt.getBitOffset()); + + Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + // Structure_1 should contain MY Foo + assertEquals(foo, dtcs[5].getDataType()); + + dtcs = foo.getComponents(); + assertEquals(5, dtcs.length); + assertTrue(dtcs[4].getDataType().isEquivalent(new FloatDataType())); + checkConflictCount(1); + } + @Test public void testEditUnions() throws Exception { @@ -896,7 +1085,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); // 2 components should get removed from CoolUnion commit = true; } @@ -988,7 +1177,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); // 2 components should get removed from CoolUnion commit = true; } @@ -1070,10 +1259,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1157,10 +1346,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1245,10 +1434,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1332,10 +1521,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1425,10 +1614,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1526,10 +1715,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitorAdapter.DUMMY); DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(dt, TaskMonitorAdapter.DUMMY); commit = true; } finally { @@ -1624,4 +1813,109 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { checkConflictCount(0); } + @Test + public void testEditUnions9() throws Exception { + + mtf.initialize("notepad", new OriginalProgramModifierListener() { + + @Override + public void modifyOriginal(ProgramDB program) throws Exception { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + + try { + Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "XYZ", 1); + enumm.add("one", 1); + enumm.add("two", 2); + enumm.add("three", 3); + dtm.addDataType( + new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm), + null); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyLatest(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + + try { + DataType enumm = dtm.getDataType(new CategoryPath("/Category1"), "XYZ"); + dtm.remove(enumm, TaskMonitor.DUMMY); + + Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), + "CoolUnion"); + // NOTE: bit field component byte sizing is currently auto-sized and packed within unions + union.insertBitField(1, IntegerDataType.dataType, 4, "bf1", "latest bf1"); + union.insertBitField(2, IntegerDataType.dataType, 2, "bf2", "latest bf2"); + commit = true; + } + catch (InvalidDataTypeException e) { + e.printStackTrace(); + Assert.fail(); + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + + try { + DataType enumm = dtm.getDataType(new CategoryPath("/Category1"), "XYZ"); + assertTrue(enumm instanceof Enum); + + Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), + "CoolUnion"); + // NOTE: bit field component byte sizing is currently auto-sized and packed within unions + union.insertBitField(1, enumm, 4, "BF1", "my bf1"); + union.insertBitField(2, enumm, 2, "BF2", "my bf2"); + + commit = true; + } + catch (InvalidDataTypeException e) { + e.printStackTrace(); + Assert.fail(); + } + finally { + program.endTransaction(transactionID, commit); + } + } + }); + executeMerge(); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + chooseOption(DataTypeMergeManager.OPTION_MY);// MY bitfields w/ enum + + waitForCompletion(); + + // primitive type of byte used in absence of enum + Union union = + (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); + //@formatter:off + assertEquals("/Category1/Category2/CoolUnion\n" + + "Unaligned\n" + + "Union CoolUnion {\n" + + " 0 qword 8 null \"\"\n" + + " 0 byte:4(4) 1 BF1 \"my bf1\"\n" + + " 0 byte:2(6) 1 BF2 \"my bf2\"\n" + + " 0 word 2 null \"\"\n" + + " 0 undefined * * * * * 4 null \"\"\n" + + " 0 DLL_Table 96 null \"\"\n" + + " 0 DLL_Table * 4 null \"\"\n" + + "}\n" + + "Size = 96 Actual Alignment = 1\n", union.toString()); + //@formatter:on + } + } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge8Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge8Test.java index 5ecce8cc6f..0747b8e996 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge8Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge8Test.java @@ -15,8 +15,7 @@ */ package ghidra.app.merge.datatypes; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import javax.swing.JLabel; @@ -37,7 +36,7 @@ import ghidra.util.task.TaskMonitorAdapter; public class DataTypeMerge8Test extends AbstractDataTypeMergeTest { @Test - public void testConflictFixUpForNonFittingStruct() throws Exception { + public void testConflictFixUpForNonFittingStruct() throws Exception { final CategoryPath miscPath = new CategoryPath("/MISC"); final CategoryPath rootPath = new CategoryPath("/"); @@ -163,7 +162,7 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest { JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel); String statusText = label.getText(); String expectedText = - "Merging Data Types: Not enough undefined bytes to fit /XYZ in structure " + + "Structure Merge: Not enough undefined bytes to fit /XYZ in structure " + "/MISC/ABC at offset 0x4.\nIt needs 3 more byte(s) to be able to fit."; assertTrue(statusText.contains(expectedText)); } 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 07be482c11..6661e444b7 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 @@ -141,7 +141,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent { } @Override - public DataType getParent() { + public Composite getParent() { return parent; } 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 1938a75e14..8b70770bf3 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 @@ -25,6 +25,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult; import ghidra.program.model.mem.MemBuffer; import ghidra.util.Msg; +import ghidra.util.exception.AssertException; import ghidra.util.exception.InvalidInputException; /** @@ -640,6 +641,13 @@ class StructureDB extends CompositeDB implements Structure { } } + /** + * Create copy of structure for target dtm (source archive information is discarded). + * WARNING! copying unaligned structures which contain bitfields can produce + * invalid results when switching endianess due to the differences in packing order. + * @param dtm target data type manager + * @return cloned structure + */ @Override public DataType copy(DataTypeManager dtm) { StructureDataType struct = @@ -649,6 +657,13 @@ class StructureDB extends CompositeDB implements Structure { return struct; } + /** + * Create cloned structure for target dtm preserving source archive information. + * WARNING! cloning unaligned structures which contain bitfields can produce + * invalid results when switching endianess due to the differences in packing order. + * @param dtm target data type manager + * @return cloned structure + */ @Override public DataType clone(DataTypeManager dtm) { StructureDataType struct = @@ -891,12 +906,28 @@ class StructureDB extends CompositeDB implements Structure { @Override public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name, String comment) { + + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); + } + + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bfDt = (BitFieldDataType) dataType; + if (length <= 0) { + length = dataType.getLength(); + } + try { + return insertBitFieldAt(offset, length, bfDt.getBitOffset(), bfDt.getBaseDataType(), + bfDt.getDeclaredBitSize(), name, comment); + } + catch (InvalidDataTypeException e) { + throw new AssertException(e); + } + } + lock.acquire(); try { checkDeleted(); - if (offset < 0) { - throw new IllegalArgumentException("Offset cannot be negative."); - } validateDataType(dataType); dataType = resolve(dataType); @@ -966,6 +997,10 @@ class StructureDB extends CompositeDB implements Structure { if (ordinal < 0 || ordinal >= numComponents) { throw new ArrayIndexOutOfBoundsException(ordinal); } + if (dataType instanceof BitFieldDataType) { + throw new IllegalArgumentException( + "Components may not be replaced with a bit-field"); + } validateDataType(dataType); DataTypeComponent origDtc = getComponent(ordinal); @@ -1085,19 +1120,14 @@ class StructureDB extends CompositeDB implements Structure { componentAdapter.removeRecord(dtc.getKey()); } components.clear(); + numComponents = 0; + structLength = 0; + if (flexibleArrayComponent != null) { flexibleArrayComponent.getDataType().removeParent(this); componentAdapter.removeRecord(flexibleArrayComponent.getKey()); flexibleArrayComponent = null; } - if (struct.isNotYetDefined()) { - numComponents = 0; - structLength = 0; - } - else { - structLength = struct.getLength(); - numComponents = isInternallyAligned() ? 0 : structLength; - } setAlignment(struct, false); @@ -1154,14 +1184,17 @@ class StructureDB extends CompositeDB implements Structure { private void doReplaceWithUnaligned(Structure struct) throws IOException { // assumes components is clear and that alignment characteristics have been set. + if (struct.isNotYetDefined()) { + return; + } - // NOTE: unaligned bitfields should remain unchanged when - // transitioning endianess even though it makes little sense. - // Unaligned structures are not intended to be portable! + structLength = struct.getLength(); + numComponents = structLength; DataTypeComponent[] otherComponents = struct.getDefinedComponents(); for (int i = 0; i < otherComponents.length; i++) { DataTypeComponent dtc = otherComponents[i]; + DataType dt = resolve(dtc.getDataType()); checkAncestry(dt); 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 a47d3402b7..0ac5e88cbc 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 @@ -196,9 +196,8 @@ public interface Structure extends Composite { public void deleteAtOffset(int offset); /** - * Remove all components from this structure, effectively setting the - * length to zero. - * + * Remove all components from this structure (including flex-array), + * effectively setting the length to zero. */ public void deleteAll(); 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 848df1fca3..140a91ff3d 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 @@ -23,6 +23,7 @@ import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult; import ghidra.program.model.mem.MemBuffer; import ghidra.util.Msg; import ghidra.util.UniversalID; +import ghidra.util.exception.AssertException; import ghidra.util.exception.InvalidInputException; /** @@ -292,9 +293,25 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length, String componentName, String comment) { + if (offset < 0) { throw new IllegalArgumentException("Offset cannot be negative."); } + + if (dataType instanceof BitFieldDataType) { + BitFieldDataType bfDt = (BitFieldDataType) dataType; + if (length <= 0) { + length = dataType.getLength(); + } + try { + return insertBitFieldAt(offset, length, bfDt.getBitOffset(), bfDt.getBaseDataType(), + bfDt.getDeclaredBitSize(), componentName, comment); + } + catch (InvalidDataTypeException e) { + throw new AssertException(e); + } + } + validateDataType(dataType); dataType = dataType.clone(dataMgr); @@ -519,7 +536,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, + public DataTypeComponentImpl insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, DataType baseDataType, int bitSize, String componentName, String comment) throws InvalidDataTypeException { @@ -842,6 +859,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return available; } + /** + * Create copy of structure for target dtm (source archive information is discarded). + * WARNING! copying unaligned structures which contain bitfields can produce + * invalid results when switching endianess due to the differences in packing order. + * @param dtm target data type manager + * @return cloned structure + */ @Override public DataType copy(DataTypeManager dtm) { StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm); @@ -850,6 +874,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return struct; } + /** + * Create cloned structure for target dtm preserving source archive information. + * WARNING! cloning unaligned structures which contain bitfields can produce + * invalid results when switching endianess due to the differences in packing order. + * @param dtm target data type manager + * @return cloned structure + */ @Override public DataType clone(DataTypeManager dtm) { if (dataMgr == dtm) { @@ -902,15 +933,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur int oldLength = structLength; components.clear(); + structLength = 0; + numComponents = 0; flexibleArrayComponent = null; - if (struct.isNotYetDefined()) { - structLength = 0; - numComponents = 0; - } - else { - structLength = struct.getLength(); - numComponents = isInternallyAligned() ? 0 : structLength; - } setAlignment(struct); @@ -945,14 +970,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur private void doReplaceWithUnaligned(Structure struct) { // assumes components is clear and that alignment characteristics have been set. + if (struct.isNotYetDefined()) { + return; + } - // NOTE: unaligned bitfields should remain unchanged when - // transitioning endianess even though it makes little sense. - // Unaligned structures are not intended to be portable! + structLength = struct.getLength(); + numComponents = structLength; DataTypeComponent[] otherComponents = struct.getDefinedComponents(); for (int i = 0; i < otherComponents.length; i++) { DataTypeComponent dtc = otherComponents[i]; + DataType dt = dtc.getDataType().clone(dataMgr); checkAncestry(dt);