diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java index 92e6623f2a..ab9f52d06a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java @@ -309,7 +309,7 @@ class StructureEditorModel extends CompEditorModel { private int[] convertRowsToOrdinals(int[] rows) { int[] ordinals = new int[rows.length]; - DataTypeComponent[] definedComponents = ((Structure) viewComposite).getDefinedComponents(); + DataTypeComponent[] definedComponents = viewComposite.getDefinedComponents(); for (int i = rows.length - 1; i >= 0; i--) { ordinals[i] = definedComponents[rows[i]].getOrdinal(); } @@ -328,7 +328,7 @@ class StructureEditorModel extends CompEditorModel { if (isShowingUndefinedBytes()) { return rowIndex; } - DataTypeComponent[] definedComponents = ((Structure) viewComposite).getDefinedComponents(); + DataTypeComponent[] definedComponents = viewComposite.getDefinedComponents(); return definedComponents[rowIndex].getOrdinal(); } @@ -737,16 +737,16 @@ class StructureEditorModel extends CompEditorModel { catch (InvalidDataTypeException e) { return false; } - + if (isPackingEnabled() || isAtEnd(rowIndex)) { return true; } - + int undefSize = getNumUndefinedBytesAfter(dtc); if (undefSize < 0) { return true; } - + int numAvailable = dtc.getLength() + undefSize; return dataType.getLength() <= numAvailable; } @@ -806,7 +806,7 @@ class StructureEditorModel extends CompEditorModel { if (isPackingEnabled() || isAtEnd(currentIndex)) { return Integer.MAX_VALUE; } - + // Can only replace with what fits unless at last component or empty last line. DataTypeComponent comp = getComponent(currentIndex); int numComponents = getNumComponents(); @@ -820,9 +820,9 @@ class StructureEditorModel extends CompEditorModel { // Otherwise, get size of component and number of Undefined bytes after it. FieldRange currentRange = getSelectedRangeContaining(currentIndex); boolean isOneComponent = - (currentRange == null) || (currentRange.getStart().getIndex().intValue() + - 1 == currentRange.getEnd().getIndex().intValue()); - + (currentRange == null) || (currentRange.getStart().getIndex().intValue() + + 1 == currentRange.getEnd().getIndex().intValue()); + if (isOneComponent) { return comp.getLength() + getNumUndefinedBytesAfter(comp); } @@ -860,7 +860,7 @@ class StructureEditorModel extends CompEditorModel { return viewDTM.withTransaction("Insert Component", () -> { DataTypeComponent dtc; if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) { - dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name, + dtc = viewComposite.insert(rowIndex, dataType, length, name, comment); } else { @@ -957,7 +957,8 @@ class StructureEditorModel extends CompEditorModel { struct.growStructure(length - avail); } } - return ((Structure) viewComposite).replace(componentOrdinal, dataType, length, name, comment); + return ((Structure) viewComposite).replace(componentOrdinal, dataType, length, + name, comment); }); } return dtc; @@ -1266,8 +1267,9 @@ class StructureEditorModel extends CompEditorModel { dialog.setStatusText("The name cannot match the external structure name."); return false; } - DataTypeManager originalDTM = getOriginalDataTypeManager(); - DataType conflictingDt = originalDTM.getDataType(getOriginalCategoryPath(), name); + + DataTypeManager originalDtm = getOriginalDataTypeManager(); + DataType conflictingDt = originalDtm.getDataType(getOriginalCategoryPath(), name); if (conflictingDt != null) { dialog.setStatusText("A data type named \"" + name + "\" already exists."); return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java index 2e5f886c08..70df062d1c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java @@ -16,11 +16,14 @@ package ghidra.app.plugin.core.compositeeditor; import java.math.BigInteger; +import java.util.*; import javax.help.UnsupportedOperationException; +import javax.swing.table.TableColumn; import docking.widgets.fieldpanel.support.FieldRange; import docking.widgets.fieldpanel.support.FieldSelection; +import docking.widgets.table.GTableHeaderRenderer; /** * Data union editor model for maintaining information about the edits being @@ -53,6 +56,9 @@ class UnionEditorModel extends CompEditorModel { private static final int DATATYPE = 2; private static final int FIELDNAME = 3; private static final int COMMENT = 4; + private static final int ORDINAL = 5; + + private List hiddenColumns; UnionEditorModel(UnionEditorProvider provider, boolean showInHex) { super(provider); @@ -62,6 +68,12 @@ class UnionEditorModel extends CompEditorModel { adjustOffsets(); this.showHexNumbers = showInHex; + List additionalColumns = new ArrayList<>(); + TableColumn ordinalColumn = new TableColumn(ORDINAL, 75); + ordinalColumn.setHeaderRenderer(new GTableHeaderRenderer()); + ordinalColumn.setHeaderValue("Ordinal"); + additionalColumns.add(ordinalColumn); + hiddenColumns = Collections.unmodifiableList(additionalColumns); } @Override @@ -69,6 +81,11 @@ class UnionEditorModel extends CompEditorModel { return "Union"; } + @Override + protected List getHiddenColumns() { + return hiddenColumns; + } + @Override public int getOffsetColumn() { return -1; @@ -99,6 +116,29 @@ class UnionEditorModel extends CompEditorModel { return COMMENT; } + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + + if ((viewComposite == null) || (rowIndex < 0) || (columnIndex < 0)) { + return ""; + } + + DataTypeComponent dtc = getComponent(rowIndex); + if (dtc == null) { + if (columnIndex == getDataTypeColumn()) { + return null; + } + return ""; + } + + if (columnIndex == ORDINAL) { + int ordinal = dtc.getOrdinal(); + return showHexNumbers ? getHexString(ordinal, true) : Integer.toString(ordinal); + } + + return super.getValueAt(rowIndex, columnIndex); + } + /** * returns whether or not a particular component row and field in this * structure is editable. @@ -381,7 +421,7 @@ class UnionEditorModel extends CompEditorModel { checkIsAllowableDataType(dataType); try { DataTypeComponent dtc = viewDTM.withTransaction("Add Component", - () -> ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment)); + () -> viewComposite.insert(rowIndex, dataType, length, name, comment)); if (rowIndex <= currentEditRow) { currentEditRow++; } @@ -418,8 +458,8 @@ class UnionEditorModel extends CompEditorModel { try { boolean isSelected = selection.containsEntirely(BigInteger.valueOf(rowIndex)); DataTypeComponent dtc = viewDTM.withTransaction("Replace Component", () -> { - ((Union) viewComposite).delete(rowIndex); - return ((Union) viewComposite).insert(rowIndex, dataType, length, name, comment); + viewComposite.delete(rowIndex); + return viewComposite.insert(rowIndex, dataType, length, name, comment); }); if (isSelected) { selection.addRange(rowIndex, rowIndex + 1); @@ -487,7 +527,7 @@ class UnionEditorModel extends CompEditorModel { @Override public void replaceOriginalComponents() { - ((Union) getOriginalComposite()).replaceWith(viewComposite); + getOriginalComposite().replaceWith(viewComposite); } @Override diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index 1c52da2ad9..ee1b12cac9 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -2285,7 +2285,13 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { monitor.checkCancelled(); if (specialTypeinfo.isInProgramMemory()) { - applyTypeinfoStructure(siClassTypeInfoStructure, specialTypeinfo.getAddress()); + Data struct = + applyTypeinfoStructure(siClassTypeInfoStructure, specialTypeinfo.getAddress()); + if (struct == null) { + Msg.error(this, + specialTypeinfo.getNamespace().getName() + ": cannot apply structure"); + continue; + } typeinfoToStructuretypeMap.put(specialTypeinfo.getAddress(), SI_CLASS_TYPE_INFO_STRUCTURE); } @@ -2725,13 +2731,19 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } - private Data applyTypeinfoStructure(Structure typeInfoStructure, Address typeinfoAddress) - throws CancelledException, AddressOutOfBoundsException, Exception { + private Data applyTypeinfoStructure(Structure typeInfoStructure, Address typeinfoAddress) { - api.clearListing(typeinfoAddress, typeinfoAddress.add(typeInfoStructure.getLength() - 1)); - Data newStructure = api.createData(typeinfoAddress, typeInfoStructure); + try { + api.clearListing(typeinfoAddress, + typeinfoAddress.add(typeInfoStructure.getLength() - 1)); + Data newStructure = api.createData(typeinfoAddress, typeInfoStructure); + return newStructure; + } + catch (CodeUnitInsertionException | CancelledException e) { + Msg.warn(this, "Could not apply typeinfo struct at " + typeinfoAddress.toString()); + return null; + } - return newStructure; } private Structure getOrCreateVmiTypeinfoStructure(Address typeinfoAddress, diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index 23019b6e1b..4d6ea41d53 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -2711,11 +2711,11 @@ public class RecoveredClassHelper { } // get only the functions from the ones that are not already processed structures - // return null if not an unprocessed table + // return null if not an unprocessed table or if invalid List virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol, allowNullFunctionPtrs, allowDefaultRefsInMiddle); - // the vftable has already been processed - skip it + // the vftable has already been processed or invalid - skip it if (virtualFunctions == null) { continue; } @@ -2769,20 +2769,19 @@ public class RecoveredClassHelper { return recoveredClasses; } - //TODO: rework above method to call this so it works with both that and other calls protected void updateClassWithVftable(RecoveredClass recoveredClass, Symbol vftableSymbol, boolean allowNullFunctionPtrs, boolean allowDefaultRefsInMiddle) throws Exception { + // get only the functions from the ones that are not already processed // structures // return null if not an unprocessed table - Address vftableAddress = vftableSymbol.getAddress(); Namespace vftableNamespace = vftableSymbol.getParentNamespace(); List virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol, allowNullFunctionPtrs, allowDefaultRefsInMiddle); - // the vftable has already been processed - skip it + // the vftable has already been processed or is invalid - skip it if (virtualFunctions == null) { return; } @@ -2797,15 +2796,10 @@ public class RecoveredClassHelper { recoveredClass.addVftableAddress(vftableAddress); recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); - // add it to the running list of RecoveredClass objects - // recoveredClasses.add(recoveredClass); } else { recoveredClass.addVftableAddress(vftableAddress); recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); -// if (!recoveredClasses.contains(recoveredClass)) { -// recoveredClasses.add(recoveredClass); -// } } @@ -2975,9 +2969,9 @@ public class RecoveredClassHelper { // pointing to are in the class already to determine size of array // create vtable - int numFunctionPointers = + Integer numFunctionPointers = createVftable(vftableAddress, allowNullFunctionPtrs, allowDefaultRefsInMiddle); - if (numFunctionPointers == 0) { + if (numFunctionPointers == null || numFunctionPointers == 0) { return null; } // make it an array @@ -3043,16 +3037,28 @@ public class RecoveredClassHelper { * @param vftableAddress the vftable address * @param allowNullFunctionPtrs if true allow vftables to have null pointers * @param allowDefaultRefsInMiddle if true allow default references into the middle of the table - * @return the created array of pointers Data or null + * @return the number of functions in the table or null if none or in invalid block * @throws CancelledException if cancelled */ - public int createVftable(Address vftableAddress, boolean allowNullFunctionPtrs, + public Integer createVftable(Address vftableAddress, boolean allowNullFunctionPtrs, boolean allowDefaultRefsInMiddle) throws CancelledException { int numFunctionPointers = 0; Address address = vftableAddress; + MemoryBlock currentBlock = program.getMemory().getBlock(vftableAddress); + if (currentBlock == null) { + Msg.warn(this, "Cannot create vftable at " + vftableAddress.toString() + + " because it is in an invalid memory block."); + return null; + } + if (currentBlock.isExternalBlock() || !currentBlock.isInitialized()) { + Msg.warn(this, "Cannot create vftable at " + vftableAddress.toString() + + " because it is in an external or an uninitialized block."); + return null; + } + boolean stillInCurrentTable = true; while (address != null && currentBlock.contains(address) && stillInCurrentTable && extendedFlatAPI.isFunctionPointer(address, allowNullFunctionPtrs)) {