GP-1793 - Updated the decompiler hover for structure fields to show the parent name and the offset in the parent

This commit is contained in:
dragonmacher 2022-03-08 17:22:19 -05:00
parent 385529aa08
commit c4054de5db
4 changed files with 207 additions and 150 deletions

View file

@ -215,7 +215,7 @@ public class DecompilerUtils {
} }
/** /**
* Returns the function represented by the given token. This will be either the * Returns the function represented by the given token. This will be either the
* decompiled function or a function referenced within the decompiled function. * decompiled function or a function referenced within the decompiled function.
* *
* @param program the program * @param program the program
@ -270,8 +270,8 @@ public class DecompilerUtils {
/** /**
* Similar to {@link #getTokens(ClangNode, AddressSetView)}, but uses the tokens from * Similar to {@link #getTokens(ClangNode, AddressSetView)}, but uses the tokens from
* the given view fields. Sometimes the tokens in the model (represented by the * the given view fields. Sometimes the tokens in the model (represented by the
* {@link ClangNode}) are different than the fields in the view (such as when a list of * {@link ClangNode}) are different than the fields in the view (such as when a list of
* comment tokens are condensed into a single comment token). * comment tokens are condensed into a single comment token).
* *
* @param fields the fields to check * @param fields the fields to check
@ -354,8 +354,7 @@ public class DecompilerUtils {
public static AddressSet findClosestAddressSet(Program program, AddressSpace functionSpace, public static AddressSet findClosestAddressSet(Program program, AddressSpace functionSpace,
List<ClangToken> tokenList) { List<ClangToken> tokenList) {
AddressSet addressSet = new AddressSet(); AddressSet addressSet = new AddressSet();
for (int i = 0; i < tokenList.size(); ++i) { for (ClangToken tok : tokenList) {
ClangToken tok = tokenList.get(i);
addTokenAddressRangeToSet(addressSet, tok, functionSpace); addTokenAddressRangeToSet(addressSet, tok, functionSpace);
} }
@ -574,8 +573,8 @@ public class DecompilerUtils {
} }
Stack<ClangSyntaxToken> braceStack = new Stack<>(); Stack<ClangSyntaxToken> braceStack = new Stack<>();
for (int i = 0; i < list.size(); ++i) { for (ClangNode element : list) {
ClangToken token = (ClangToken) list.get(i); ClangToken token = (ClangToken) element;
if (token instanceof ClangSyntaxToken) { if (token instanceof ClangSyntaxToken) {
ClangSyntaxToken syntaxToken = (ClangSyntaxToken) token; ClangSyntaxToken syntaxToken = (ClangSyntaxToken) token;
@ -637,7 +636,7 @@ public class DecompilerUtils {
* sequence of tokens that are part of the comment and group them into a single * sequence of tokens that are part of the comment and group them into a single
* ClangCommentToken. This makes post processing on the full comment string easier. * ClangCommentToken. This makes post processing on the full comment string easier.
* A single comment string can contain white space that manifests as ClangSyntaxTokens * A single comment string can contain white space that manifests as ClangSyntaxTokens
* with white space as text. * with white space as text.
* @param alltoks is the token stream * @param alltoks is the token stream
* @param i is the position of the initial comment token * @param i is the position of the initial comment token
* @param first is the initial comment token * @param first is the initial comment token
@ -750,6 +749,17 @@ public class DecompilerUtils {
token = context.getTokenAtCursor(); token = context.getTokenAtCursor();
} }
return getDataType(token);
}
/**
* Returns the data type for the given token
*
* @param token the token
* @return the data type or null
*/
public static DataType getDataType(ClangToken token) {
Varnode varnode = DecompilerUtils.getVarnodeRef(token); Varnode varnode = DecompilerUtils.getVarnodeRef(token);
if (varnode != null) { if (varnode != null) {
HighVariable highVariable = varnode.getHigh(); HighVariable highVariable = varnode.getHigh();

View file

@ -20,16 +20,19 @@ import javax.swing.JComponent;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangTextField; import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.plugin.core.hover.AbstractConfigurableHover; import ghidra.app.plugin.core.hover.AbstractConfigurableHover;
import ghidra.app.util.ToolTipUtils; import ghidra.app.util.ToolTipUtils;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighVariable;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.HTMLUtilities;
import ghidra.util.NumericUtilities;
public class DataTypeDecompilerHover extends AbstractConfigurableHover public class DataTypeDecompilerHover extends AbstractConfigurableHover
implements DecompilerHoverService { implements DecompilerHoverService {
@ -73,44 +76,77 @@ public class DataTypeDecompilerHover extends AbstractConfigurableHover
} }
ClangToken token = ((ClangTextField) field).getToken(fieldLocation); ClangToken token = ((ClangTextField) field).getToken(fieldLocation);
DataType dt = DecompilerUtils.getDataType(token);
DataType dt = getDataType(token);
if (dt == null) { if (dt == null) {
dt = getDataType(token.Parent()); return null;
} }
if (dt != null) { String toolTipText = null;
String toolTipText = ToolTipUtils.getToolTipText(dt); if (token instanceof ClangFieldToken) {
return createTooltipComponent(toolTipText); toolTipText = createFieldToolTipText((ClangFieldToken) token, dt);
}
else {
toolTipText = ToolTipUtils.getToolTipText(dt);
} }
return null;
return createTooltipComponent(toolTipText);
} }
private DataType getDataType(ClangNode node) { private String createFieldToolTipText(ClangFieldToken token, DataType parentType) {
ClangFieldToken fieldToken = token;
int offset = fieldToken.getOffset();
DataType fieldType = getFieldDataType(fieldToken);
if (node instanceof ClangVariableDecl) { //
return ((ClangVariableDecl) node).getDataType(); // Parent: BarBar
// Offset: 0x8
// Field Name: fooField
//
String BR = HTMLUtilities.BR;
StringBuilder newContent = new StringBuilder();
newContent.append("<TABLE>");
//@formatter:off
newContent.append(
row("Parent: ", HTMLUtilities.friendlyEncodeHTML(parentType.getName())));
newContent.append(
row("Offset: ", NumericUtilities.toHexString(offset)));
newContent.append(
row("Field Name: ", HTMLUtilities.friendlyEncodeHTML(token.getText())));
//@formatter:on
newContent.append("</TABLE>");
newContent.append(BR).append("<HR WIDTH=\"95%\">").append(BR);
String toolTipText = ToolTipUtils.getToolTipText(fieldType);
StringBuilder buffy = new StringBuilder(toolTipText);
int start = HTMLUtilities.HTML.length();
buffy.insert(start, newContent);
return buffy.toString();
}
private String row(String... cols) {
StringBuilder sb = new StringBuilder("<TR>");
for (String col : cols) {
sb.append("<TD>").append(col).append("</TD>");
} }
sb.append("</TR>");
return sb.toString();
}
if (node instanceof ClangReturnType) { public static DataType getFieldDataType(ClangFieldToken field) {
return ((ClangReturnType) node).getDataType(); DataType fieldDt = DataTypeUtils.getBaseDataType(field.getDataType());
} if (fieldDt instanceof Structure) {
Structure parent = (Structure) fieldDt;
if (node instanceof ClangTypeToken) { int offset = field.getOffset();
return ((ClangTypeToken) node).getDataType(); int n = parent.getLength();
} if (offset >= 0 && offset < n) {
DataTypeComponent dtc = parent.getComponentAt(offset);
if (node instanceof ClangVariableToken) { fieldDt = dtc.getDataType();
Varnode vn = ((ClangVariableToken) node).getVarnode();
if (vn != null) {
HighVariable high = vn.getHigh();
if (high != null) {
return high.getDataType();
}
} }
} }
return fieldDt;
return null;
} }
} }

View file

@ -25,7 +25,7 @@ import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
/** /**
* A base class that represents a variable from the decompiler. This is either a variable * A base class that represents a variable from the decompiler. This is either a variable
* type or a variable with an optional field access. * type or a variable with an optional field access.
*/ */
public abstract class DecompilerVariable { public abstract class DecompilerVariable {
@ -51,7 +51,7 @@ public abstract class DecompilerVariable {
return ((ClangTypeToken) variable).getDataType(); return ((ClangTypeToken) variable).getDataType();
} }
// not sure if we need this; the type returned here is the structure and not the // not sure if we need this; the type returned here is the structure and not the
// field's type // field's type
// if (variable instanceof ClangFieldToken) { // if (variable instanceof ClangFieldToken) {
// return ((ClangFieldToken) variable).getDataType(); // return ((ClangFieldToken) variable).getDataType();
@ -79,8 +79,8 @@ public abstract class DecompilerVariable {
} }
} }
// Prefer the type of the first input varnode, unless that type is a 'void *'. // Prefer the type of the first input varnode, unless that type is a 'void *'.
// Usually, in that special case, the output varnode has the correct type information. // Usually, in that special case, the output varnode has the correct type information.
PcodeOp op = variable.getPcodeOp(); PcodeOp op = variable.getPcodeOp();
dataType = getInputDataType(op); dataType = getInputDataType(op);
@ -193,7 +193,7 @@ public abstract class DecompilerVariable {
String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n"; String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n";
//@formatter:off //@formatter:off
return "{\n" + return "{\n" +
castString + castString +
"\tvariable: " + variable + ",\n" + "\tvariable: " + variable + ",\n" +
"}"; "}";
//@formatter:on //@formatter:on

View file

@ -27,11 +27,10 @@ import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.task.TaskMonitor;
/** /**
* Structure implementation for the Database. * Structure database implementation.
*
*
*/ */
class StructureDB extends CompositeDB implements StructureInternal { class StructureDB extends CompositeDB implements StructureInternal {
@ -42,15 +41,6 @@ class StructureDB extends CompositeDB implements StructureInternal {
private int numComponents; // If packed, this does not include the undefined components. private int numComponents; // If packed, this does not include the undefined components.
private List<DataTypeComponentDB> components; private List<DataTypeComponentDB> components;
/**
* Constructor
*
* @param dataMgr
* @param cache
* @param compositeAdapter
* @param componentAdapter
* @param record
*/
public StructureDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, public StructureDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache,
CompositeDBAdapter compositeAdapter, ComponentDBAdapter componentAdapter, CompositeDBAdapter compositeAdapter, ComponentDBAdapter componentAdapter,
DBRecord record) { DBRecord record) {
@ -95,35 +85,36 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Eliminate use of old trailing flex-array specification which is now specified using * Eliminate use of old trailing flex-array specification which is now specified using a
* a zero-element array. Due to the specification of a new array datatype this must handle * zero-element array. Due to the specification of a new array datatype this must handle two
* two cases when an old flex-array component is exists: * cases when an old flex-array component is exists:
* <ol> * <ol>
* <li>read-only case: associated {@link DataTypeManagerDB} is not updateable. This is the normal * <li>read-only case: associated {@link DataTypeManagerDB} is not updatable. This is the
* case for an open archive. A non-DB ArrayDataType must be employed with an immutable * normal case for an open archive. A non-DB ArrayDataType must be employed with an immutable
* {@link DataTypeProxyComponentDB} in place of the flex-array component.</li> * {@link DataTypeProxyComponentDB} in place of the flex-array component.</li>
* <li>upgrade (open for update with open transaction): the flex-array record is modified to * <li>upgrade (open for update with open transaction): the flex-array record is modified to
* to indicate an appropriate resolved zero-element array. * indicate an appropriate resolved zero-element array.
* </ol> * </ol>
* <p> * <p>
* NOTE: When {@link DataTypeManagerDB} is instantiated for update an upgrade must be forced based upon the * NOTE: When {@link DataTypeManagerDB} is instantiated for update an upgrade must be forced
* {@link CompositeDBAdapter#isFlexArrayMigrationRequired()} indicator. The upgrade logic * based upon the {@link CompositeDBAdapter#isFlexArrayMigrationRequired()} indicator. The
* (see {@link DataTypeManagerDB#migrateOldFlexArrayComponentsIfRequired(ghidra.util.task.TaskMonitor)}) * upgrade logic(see
* istantiates all structures within the databases with an open transaaction allowing this method * {@link DataTypeManagerDB#migrateOldFlexArrayComponentsIfRequired(TaskMonitor)}) instantiates
* to perform the neccessary flex-array record migration. * all structures within the databases with an open transaction allowing this method to perform
* the necessary flex-array record migration.
* <p> * <p>
* NOTE: The offset of the migrated flex array component and structure length may change during upgrade * NOTE: The offset of the migrated flex array component and structure length may change during
* when packing is enabled when the original packed structure length did not properly factor the flex-array * upgrade when packing is enabled when the original packed structure length did not properly
* alignment. Repack does not occur in the read-only case. * factor the flex-array alignment. Repack does not occur in the read-only case.
* *
* @param oldFlexArrayRecord record which corresponds to an olf flex-array component * @param oldFlexArrayRecord record which corresponds to an old flex-array component
* @throws IOException if a database error occurs * @throws IOException if a database error occurs
*/ */
private void migrateOldFlexArray(DBRecord oldFlexArrayRecord) throws IOException { private void migrateOldFlexArray(DBRecord oldFlexArrayRecord) throws IOException {
long id = oldFlexArrayRecord.getLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL); long id = oldFlexArrayRecord.getLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL);
DataType dt = dataMgr.getDataType(id); // could be BadDataType if built-in type is missing DataType dt = dataMgr.getDataType(id); // could be BadDataType if built-in type is missing
// use zero-element array (need positive element length if dt is BadDataType) // use zero-element array (need positive element length if type is BadDataType)
dt = new ArrayDataType(dt, 0, 1, dataMgr); dt = new ArrayDataType(dt, 0, 1, dataMgr);
boolean repack = false; boolean repack = false;
@ -206,13 +197,14 @@ class StructureDB extends CompositeDB implements StructureInternal {
DataTypeComponentDB dtc = null; DataTypeComponentDB dtc = null;
try { try {
if (dataType == DataType.DEFAULT) { if (dataType == DataType.DEFAULT) {
// assume non-packed structure - structre will grow by 1-byte below // assume non-packed structure - structure will grow by 1-byte below
dtc = new DataTypeComponentDB(dataMgr, this, numComponents, structLength); dtc = new DataTypeComponentDB(dataMgr, this, numComponents, structLength);
} }
else { else {
int componentLength = getPreferredComponentLength(dataType, length); int componentLength = getPreferredComponentLength(dataType, length);
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, DBRecord rec =
componentLength, numComponents, structLength, name, comment); componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key,
componentLength, numComponents, structLength, name, comment);
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
dataType.addParent(this); dataType.addParent(this);
components.add(dtc); components.add(dtc);
@ -327,8 +319,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
length = getPreferredComponentLength(dataType, length); length = getPreferredComponentLength(dataType, length);
int offset = getComponent(ordinal).getOffset(); int offset = getComponent(ordinal).getOffset();
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length, DBRecord rec =
ordinal, offset, name, comment); componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length,
ordinal, offset, name, comment);
DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
dataType.addParent(this); dataType.addParent(this);
shiftOffsets(idx, 1, dtc.getLength()); shiftOffsets(idx, 1, dtc.getLength());
@ -553,8 +546,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Removes a defined component at the specified index without * Removes a defined component at the specified index without any alteration to other components.
* any alteration to other components.
* @param index defined component index * @param index defined component index
* @return the defined component which was removed. * @return the defined component which was removed.
* @throws IOException if an IO error occurs * @throws IOException if an IO error occurs
@ -568,15 +560,15 @@ class StructureDB extends CompositeDB implements StructureInternal {
/** /**
* Removes a defined component at the specified index. * Removes a defined component at the specified index.
* If this corresponds to a zero-length or bit-field component it will * <p>
* be cleared without an offset shift to the remaining components. Removal of * If this corresponds to a zero-length or bit-field component it will be cleared without an
* other component types will result in an offset and ordinal shift * offset shift to the remaining components. Removal of other component types will result in
* to the remaining components. In the case of a non-packed * an offset and ordinal shift to the remaining components. In the case of a non-packed
* structure, the resulting shift will cause in a timestamp change * structure, the resulting shift will cause in a timestamp change for this structure.
* for this structure. *
* @param index defined component index * @param index defined component index
* @param disableOffsetShift if false, and component is not a bit-field, an offset shift * @param disableOffsetShift if false, and component is not a bit-field, an offset shift and
* and possible structure length change will be performed for non-packed structure. * possible structure length change will be performed for non-packed structure.
*/ */
private void doDeleteWithComponentShift(int index, boolean disableOffsetShift) { private void doDeleteWithComponentShift(int index, boolean disableOffsetShift) {
DataTypeComponentDB dtc = null; DataTypeComponentDB dtc = null;
@ -657,8 +649,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
} }
// update numComponents only
components = newComponents; components = newComponents;
updateComposite(numComponents + ordinalAdjustment, -1, -1, true); // update numComponents only updateComposite(numComponents + ordinalAdjustment, -1, -1, true);
if (isPackingEnabled()) { if (isPackingEnabled()) {
if (!repack(false, true)) { if (!repack(false, true)) {
@ -666,10 +659,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
} }
else { else {
updateComposite(-1, structLength + offsetAdjustment, -1, true); // update length only // update length only
updateComposite(-1, structLength + offsetAdjustment, -1, true);
if (bitFieldRemoved) { if (bitFieldRemoved) {
repack(false, false); repack(false, false);
} }
notifySizeChanged(false); notifySizeChanged(false);
} }
} }
@ -781,10 +775,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Create copy of structure for target dtm (source archive information is discarded). * Create copy of structure for target data type manager (source archive information is
* discarded).
* <p> * <p>
* WARNING! copying non-packed structures which contain bitfields can produce invalid results when * WARNING! copying non-packed structures which contain bitfields can produce invalid results
* switching endianess due to the differences in packing order. * when switching endianness due to the differences in packing order.
* *
* @param dtm target data type manager * @param dtm target data type manager
* @return cloned structure * @return cloned structure
@ -799,9 +794,10 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Create cloned structure for target dtm preserving source archive information. WARNING! * Create cloned structure for target data type manager preserving source archive information.
* cloning non-packed structures which contain bitfields can produce invalid results when * <p>
* switching endianess due to the differences in packing order. * WARNING! cloning non-packed structures which contain bitfields can produce invalid results
* when switching endianness due to the differences in packing order.
* *
* @param dtm target data type manager * @param dtm target data type manager
* @return cloned structure * @return cloned structure
@ -884,7 +880,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
throw new IndexOutOfBoundsException(ordinal); throw new IndexOutOfBoundsException(ordinal);
} }
int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), int idx = Collections.binarySearch(components, Integer.valueOf(ordinal),
OrdinalComparator.INSTANCE); OrdinalComparator.INSTANCE);
if (idx >= 0) { if (idx >= 0) {
DataTypeComponentDB dtc = components.remove(idx); DataTypeComponentDB dtc = components.remove(idx);
dtc.getDataType().removeParent(this); dtc.getDataType().removeParent(this);
@ -914,9 +910,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Backup from specified defined-component index to the first component which contains the specified offset. * Backup from specified defined-component index to the first component which contains the
* @param index any defined component index which contains offset * specified offset.
* @param offset offset within structure *
* @param index any defined component index which contains offset.
* @param offset offset within structure.
* @return index of first defined component containing specific offset. * @return index of first defined component containing specific offset.
*/ */
private int backupToFirstComponentContainingOffset(int index, int offset) { private int backupToFirstComponentContainingOffset(int index, int offset) {
@ -934,10 +932,12 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Identify defined-component index of the first non-zero-length component which contains the specified offset. * Identify defined-component index of the first non-zero-length component which contains the
* If only zero-length components exist, the last zero-length component which contains the offset will be returned. * specified offset. If only zero-length components exist, the last zero-length component which
* @param index any defined component index which contains offset * contains the offset will be returned.
* @param offset offset within structure *
* @param index any defined component index which contains offset.
* @param offset offset within structure.
* @return index of first defined component containing specific offset. * @return index of first defined component containing specific offset.
*/ */
private int indexOfFirstNonZeroLenComponentContainingOffset(int index, int offset) { private int indexOfFirstNonZeroLenComponentContainingOffset(int index, int offset) {
@ -954,9 +954,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Advance from specified defined-component index to the last component which contains the specified offset. * Advance from specified defined-component index to the last component which contains the
* @param index any defined component index which contains offset * specified offset.
* @param offset offset within structure *
* @param index any defined component index which contains offset.
* @param offset offset within structure.
* @return index of last defined component containing specific offset. * @return index of last defined component containing specific offset.
*/ */
private int advanceToLastComponentContainingOffset(int index, int offset) { private int advanceToLastComponentContainingOffset(int index, int offset) {
@ -993,7 +995,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
shiftOffsets(-index - 1, -1, -1); // updates timestamp shiftOffsets(-index - 1, -1, -1); // updates timestamp
} }
else { else {
// delete all components containing offset working backward from last such component // delete all components containing the offset working backward from the last such
// component
index = advanceToLastComponentContainingOffset(index, offset); index = advanceToLastComponentContainingOffset(index, offset);
DataTypeComponentDB dtc = components.get(index); DataTypeComponentDB dtc = components.get(index);
while (dtc.containsOffset(offset)) { while (dtc.containsOffset(offset)) {
@ -1011,7 +1014,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
lock.release(); lock.release();
} }
} }
@Override @Override
public void clearAtOffset(int offset) { public void clearAtOffset(int offset) {
lock.acquire(); lock.acquire();
@ -1109,7 +1112,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
lock.release(); lock.release();
} }
} }
@Override @Override
public List<DataTypeComponent> getComponentsContaining(int offset) { public List<DataTypeComponent> getComponentsContaining(int offset) {
lock.acquire(); lock.acquire();
@ -1141,8 +1144,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
if (!hasSizedComponent && offset != structLength && !isPackingEnabled()) { if (!hasSizedComponent && offset != structLength && !isPackingEnabled()) {
// generate undefined componentfor padding offset within non-packed structure // generate undefined component for padding offset within non-packed structure if
// if offset only occupied by zero-length component // offset only occupied by zero-length component
list.add(generateUndefinedComponent(offset, index)); list.add(generateUndefinedComponent(offset, index));
} }
return list; return list;
@ -1155,8 +1158,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
/** /**
* Generate an undefined component following a binary search across the defined components. * Generate an undefined component following a binary search across the defined components.
* @param offset the offset within this structure which was searched for * @param offset the offset within this structure which was searched for
* @param missingComponentIndex the defined component binary search index result (must be negative) * @param missingComponentIndex the defined component binary search index result (must be
* @return undefined component * negative)
* @return undefined component
*/ */
private DataTypeComponentDB generateUndefinedComponent(int offset, int missingComponentIndex) { private DataTypeComponentDB generateUndefinedComponent(int offset, int missingComponentIndex) {
if (missingComponentIndex >= 0) { if (missingComponentIndex >= 0) {
@ -1276,8 +1280,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
length = getPreferredComponentLength(dataType, length); length = getPreferredComponentLength(dataType, length);
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length, DBRecord rec =
ordinal, offset, name, comment); componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length,
ordinal, offset, name, comment);
dataType.addParent(this); dataType.addParent(this);
DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
shiftOffsets(index, 1 + additionalShift, length + additionalShift); shiftOffsets(index, 1 + additionalShift, length + additionalShift);
@ -1335,15 +1340,16 @@ class StructureDB extends CompositeDB implements StructureInternal {
// defined component // defined component
DataTypeComponentDB origDtc = components.get(index); DataTypeComponentDB origDtc = components.get(index);
offset = origDtc.getOffset(); offset = origDtc.getOffset();
if (isPackingEnabled() || length == 0) { if (isPackingEnabled() || length == 0) {
// case 1: packed structure or zero-length replacement - do 1-for-1 replacement // case 1: packed structure or zero-length replacement - do 1-for-1 replacement
replacedComponents.add(origDtc); replacedComponents.add(origDtc);
} }
else if (origDtc.getLength() == 0) { else if (origDtc.getLength() == 0) {
// case 2: replaced component is zero-length (like-for-like replacement handled by case 1 above // case 2: replaced component is zero-length (like-for-like replacement handled
throw new IllegalArgumentException( // by case 1 above
"Zero-length component may only be replaced with another zero-length component"); throw new IllegalArgumentException("Zero-length component may only be " +
"replaced with another zero-length component");
} }
else if (origDtc.isBitFieldComponent()) { else if (origDtc.isBitFieldComponent()) {
// case 3: replacing bit-field (must replace all bit-fields which overlap) // case 3: replacing bit-field (must replace all bit-fields which overlap)
@ -1371,7 +1377,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
} }
else { else {
// case 4: sized component replacemnt - do 1-for-1 replacement // case 4: sized component replacement - do 1-for-1 replacement
replacedComponents.add(origDtc); replacedComponents.add(origDtc);
} }
} }
@ -1424,11 +1430,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
if (offset < 0) { if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative."); throw new IllegalArgumentException("Offset cannot be negative.");
} }
lock.acquire(); lock.acquire();
try { try {
checkDeleted(); checkDeleted();
if (offset >= structLength) { if (offset >= structLength) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Offset " + offset + " is beyond end of structure (" + structLength + ")."); "Offset " + offset + " is beyond end of structure (" + structLength + ").");
@ -1452,15 +1458,17 @@ class StructureDB extends CompositeDB implements StructureInternal {
// case 1: only defined component(s) at offset are zero-length // case 1: only defined component(s) at offset are zero-length
if (origDtc.getLength() == 0) { if (origDtc.getLength() == 0) {
if (isPackingEnabled()) { if (isPackingEnabled()) {
// if packed: insert after zero-length component // if packed: insert after zero-length component
return insert(index + 1, dataType, length, name, comment); return insert(index + 1, dataType, length, name, comment);
} }
// if non-packed: replace undefined component which immediately follows the zero-length component // if non-packed: replace undefined component which immediately follows the
// zero-length component
replacedComponents.add( replacedComponents.add(
new DataTypeComponentDB(dataMgr, this, origDtc.getOrdinal() + 1, offset)); new DataTypeComponentDB(dataMgr, this, origDtc.getOrdinal() + 1, offset));
} }
// case 2: sized component at offset is bit-field (must replace all bit-fields which contain offset) // case 2: sized component at offset is bit-field (must replace all bit-fields
// which contain offset)
else if (origDtc.isBitFieldComponent()) { else if (origDtc.isBitFieldComponent()) {
replacedComponents.add(origDtc); replacedComponents.add(origDtc);
for (int i = index - 1; i >= 0; i--) { for (int i = index - 1; i >= 0; i--) {
@ -1482,11 +1490,13 @@ class StructureDB extends CompositeDB implements StructureInternal {
index = -index - 1; index = -index - 1;
if (isPackingEnabled()) { if (isPackingEnabled()) {
// case 4: if replacing padding for packed struction perform insert at correct ordinal // case 4: if replacing padding for packed structure perform insert at correct
// ordinal
return insert(index, dataType, length, name, comment); return insert(index, dataType, length, name, comment);
} }
// case 5: replace undefined component at offset - compute undefined component to be replaced // case 5: replace undefined component at offset - compute undefined component to
// be replaced
int ordinal = offset; int ordinal = offset;
if (index > 0) { if (index > 0) {
// use previous defined component to determine ordinal for undefined component // use previous defined component to determine ordinal for undefined component
@ -1529,10 +1539,10 @@ class StructureDB extends CompositeDB implements StructureInternal {
* *
* @param dataType the structure to get the component information from. * @param dataType the structure to get the component information from.
* @throws IllegalArgumentException if any of the component data types are not allowed to * @throws IllegalArgumentException if any of the component data types are not allowed to
* replace a component in this composite data type. For example, suppose dt1 * replace a component in this composite data type. For example, suppose dt1 contains dt2.
* contains dt2. Therefore it is not valid to replace a dt2 component with dt1 since * Therefore it is not valid to replace a dt2 component with dt1 since this would cause a
* this would cause a cyclic dependency. * cyclic dependency.
* @see ghidra.program.database.data.DataTypeDB#replaceWith(ghidra.program.model.data.DataType) * @see DataTypeDB#replaceWith(DataType)
*/ */
@Override @Override
public void replaceWith(DataType dataType) { public void replaceWith(DataType dataType) {
@ -1647,7 +1657,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
DataType dt = resolvedDts[i]; // ancestry check already performed by caller DataType dt = resolvedDts[i]; // ancestry check already performed by caller
int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength();
if (length < 0 || dtc.isBitFieldComponent()) { if (length < 0 || dtc.isBitFieldComponent()) {
// TODO: bitfield truncation/expansion may be an issues if data organization changes // TODO: bitfield truncation/expansion may be an issue if data organization changes
length = dtc.getLength(); length = dtc.getLength();
} }
else { else {
@ -1967,10 +1977,10 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Check for available undefined bytes within a non-packed structure for a component * Check for available undefined bytes within a non-packed structure for a component update
* update with the specified ordinal. * with the specified ordinal.
* @param lastOrdinalReplacedOrUpdated the ordinal of a component to be updated * @param lastOrdinalReplacedOrUpdated the ordinal of a component to be updated or the last
* or the last ordinal with in a sequence of components being replaced. * ordinal with in a sequence of components being replaced.
* @param bytesNeeded number of additional bytes required to complete operation * @param bytesNeeded number of additional bytes required to complete operation
* @throws IllegalArgumentException if unable to identify/make sufficient space * @throws IllegalArgumentException if unable to identify/make sufficient space
*/ */
@ -1995,20 +2005,21 @@ class StructureDB extends CompositeDB implements StructureInternal {
/** /**
* Replace the specified components with a new component containing the specified data type. * Replace the specified components with a new component containing the specified data type.
* If {@link DataType#DEFAULT} is specified as the resolvedDataType only a clear operation * If {@link DataType#DEFAULT} is specified as the resolvedDataType only a clear operation
* is performed. * is performed.
* *
* @param origComponents the original sequence of data type components in this structure * @param origComponents the original sequence of data type components in this structure to be
* to be replaced. These components must be adjacent components in sequential order. * replaced. These components must be adjacent components in sequential order. If an
* If an non-packed undefined component is specified no other component may be included. * non-packed undefined component is specified no other component may be included.
* @param resolvedDataType the data type of the new component * @param resolvedDataType the data type of the new component
* @param newOffset offset of replacement component which must fall within origComponents bounds * @param newOffset offset of replacement component which must fall within origComponents
* bounds
* @param length the length of the new component * @param length the length of the new component
* @param name the field name of the new component * @param name the field name of the new component
* @param comment the comment for the new component * @param comment the comment for the new component
* @return the new component or null if only a clear operation was performed. * @return the new component or null if only a clear operation was performed.
* @throws IOException if database IO error occurs * @throws IOException if database IO error occurs
* @throws IllegalArgumentException if unable to identify/make sufficient space * @throws IllegalArgumentException if unable to identify/make sufficient space
*/ */
private DataTypeComponent replaceComponents(LinkedList<DataTypeComponentDB> origComponents, private DataTypeComponent replaceComponents(LinkedList<DataTypeComponentDB> origComponents,
DataType resolvedDataType, int newOffset, int length, String name, String comment) DataType resolvedDataType, int newOffset, int length, String name, String comment)
@ -2151,8 +2162,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
checkAncestry(replacementDt); checkAncestry(replacementDt);
} }
catch (Exception e) { catch (Exception e) {
// TODO: should we use Undefined1 instead to avoid cases where // TODO: should we use Undefined1 instead to avoid cases where DEFAULT datatype can
// DEFAULT datatype can not be used (bitfield, aligned structure, etc.) // not be used (bitfield, aligned structure, etc.)
// TODO: failing silently is rather hidden // TODO: failing silently is rather hidden
replacementDt = DataType.DEFAULT; replacementDt = DataType.DEFAULT;
} }
@ -2293,8 +2304,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
} }
/** /**
* Perform structure member repack. * Perform structure member repack. Perform lazy update of stored alignment introduced with v5
* Perform lazy update of stored alignment introduced with v5 adapter. * adapter.
*/ */
@Override @Override
protected boolean repack(boolean isAutoChange, boolean notify) { protected boolean repack(boolean isAutoChange, boolean notify) {
@ -2305,9 +2316,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
int oldLength = structLength; int oldLength = structLength;
int oldAlignment = getComputedAlignment(true); // ensure that alignment has been stored int oldAlignment = getComputedAlignment(true); // ensure that alignment has been stored
computedAlignment = -1; // clear cached alignment computedAlignment = -1; // clear cached alignment
boolean changed; boolean changed;
if (!isPackingEnabled()) { if (!isPackingEnabled()) {
changed = adjustNonPackedComponents(!isAutoChange); changed = adjustNonPackedComponents(!isAutoChange);
@ -2319,7 +2330,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
changed |= updateComposite(components.size(), packResult.structureLength, changed |= updateComposite(components.size(), packResult.structureLength,
packResult.alignment, !isAutoChange); packResult.alignment, !isAutoChange);
} }
if (changed && notify) { if (changed && notify) {
if (oldLength != structLength) { if (oldLength != structLength) {
notifySizeChanged(isAutoChange); notifySizeChanged(isAutoChange);