GP-0 Added FindDataTypeConflictCauseScript

This commit is contained in:
ghidra1
2023-08-02 14:47:01 -04:00
parent 87be0574fc
commit 8073611e66
@@ -0,0 +1,308 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Search for the root cause of a datatype conflict based upon a selected datatype.
//@category Data Types
import java.util.*;
import javax.help.UnsupportedOperationException;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
public class FindDataTypeConflictCauseScript extends GhidraScript {
private DataTypeManager dtm;
private HashSet<Integer> previouslyDetectedPairHashes;
private boolean hasReport;
@Override
protected void run() throws Exception {
previouslyDetectedPairHashes = new HashSet<>();
PluginTool tool = state.getTool();
if (tool == null) {
throw new UnsupportedOperationException("headless use not supported");
}
DataTypeManagerService dtmService = tool.getService(DataTypeManagerService.class);
if (dtmService == null) {
popup("Tool does not contain a DataTypeManagerService!");
return;
}
List<DataType> selectedDatatypes = dtmService.getSelectedDatatypes();
if (selectedDatatypes.size() != 1 || !(selectedDatatypes.get(0) instanceof Composite)) {
popup("Select a single conflict datatype before running script");
return;
}
DataType selectedDt = DataTypeUtilities.getBaseDataType(selectedDatatypes.get(0));
if (selectedDt instanceof Pointer || selectedDt instanceof Array) {
popup("Selected datatype must not be a Pointer or Array");
return;
}
dtm = selectedDt.getDataTypeManager();
Category cat = dtm.getCategory(selectedDt.getCategoryPath());
int count = 0;
for (DataType dt : cat.getDataTypesByBaseName(selectedDt.getName())) {
if (dt == selectedDt || dt instanceof Pointer || dt instanceof Array) {
continue;
}
++count;
findRootDifferences(null, selectedDt, null, dt);
}
if (!hasReport) {
println(
"No apparent differences found when checking " + count + " type conflicts for '" +
DataTypeUtilities.getNameWithoutConflict(selectedDt, false) + "'");
if (count != 0) {
println(
"NOTE: This may be the result of a known resolve bug where a conflict decision\n" +
"was made prior to resolving a referenced datatype (see GP-3632).");
}
}
}
private void findRootDifferences(DataType p1, DataType dt1, DataType p2, DataType dt2) {
if (dt1 == dt2) {
return;
}
if (!previouslyDetectedPairHashes.add(getPairHash(dt1, dt2))) {
return; // skip if previously checked
}
// System.out.println("Compare " + dt1.getName() + " with " + dt2.getName());
if (!DataTypeUtilities.getNameWithoutConflict(dt1, true)
.equals(DataTypeUtilities.getNameWithoutConflict(dt2, true))) {
report("Referenced type pathnames differ", p1, dt1, p2, dt2);
return;
}
if (dt1.getClass() != dt2.getClass()) {
report("Types are fundamentally different", p1, dt1, p2, dt2);
return;
}
if (dt1 instanceof Structure s1) {
findRootCompositeDifferences(p1, s1, p2, (Structure) dt2);
}
else if (dt1 instanceof Union u1) {
findRootCompositeDifferences(p1, u1, p2, (Union) dt2);
}
else if (dt1 instanceof FunctionDefinition f1) {
findRootFunctionDifferences(p1, f1, p2, (FunctionDefinition) dt2);
}
else if (dt1 instanceof TypeDef t1) {
TypeDef t2 = (TypeDef) dt2;
findRootDifferences(t1, t1.getDataType(), t2, t2.getDataType());
}
else if (dt1 instanceof Enum e1) {
if (!e1.isEquivalent(dt2)) {
report("Enums are different", p1, dt1, p2, dt2);
}
}
else if (dt1 instanceof Pointer ptr1 && dt2 instanceof Pointer ptr2) {
if (ptr1.hasLanguageDependantLength() != ptr2.hasLanguageDependantLength()) {
String detail = "(" +
(ptr1.hasLanguageDependantLength() ? "dynamic"
: Integer.toString(8 * ptr1.getLength())) +
"/" + (ptr2.hasLanguageDependantLength() ? "dynamic"
: Integer.toString(8 * ptr2.getLength())) +
")";
report("Pointers are not both dynamic-length " + detail, p1, dt1, p2, dt2);
}
else if (ptr1.getLength() != ptr2.getLength()) {
String detail = "(" + Integer.toString(8 * ptr1.getLength()) + "/" +
Integer.toString(8 * ptr2.getLength()) + ")";
report("Fixed-length pointers vary in length " + detail, p1, dt1, p2, dt2);
}
else {
findRootDifferences(p1, ptr1.getDataType(), p2, ptr2.getDataType());
}
}
else if (dt1 instanceof Array a1 && dt2 instanceof Array a2) {
if (a1.getNumElements() != a2.getNumElements()) {
report("Array dimensions differ", p1, dt1, p2, dt2);
}
else {
findRootDifferences(p1, a1.getDataType(), p2, a2.getDataType());
}
}
else if (!dt1.isEquivalent(dt2)) {
report("Types are not equivalent", p1, dt1, p2, dt2);
}
}
private void findRootFunctionDifferences(DataType p1, FunctionDefinition f1, DataType p2,
FunctionDefinition f2) {
boolean bailout = false;
StringBuilder buf = null;
if (!Objects.equals(f1.getCallingConventionName(), f2.getCallingConventionName())) {
buf = appendMsg(buf, "Calling conventions (" + f1.getCallingConventionName() + "/" +
f2.getCallingConventionName() + ")");
}
if (f1.hasNoReturn() != f2.hasNoReturn()) {
buf =
appendMsg(buf, "Has noreturn (" + f1.hasNoReturn() + "/" + f2.hasNoReturn() + ")");
}
if (f1.hasVarArgs() != f2.hasVarArgs()) {
buf = appendMsg(buf, "Has varargs (" + f1.hasVarArgs() + "/" + f2.hasVarArgs() + ")");
bailout = true;
}
ParameterDefinition[] args1 = f1.getArguments();
ParameterDefinition[] args2 = f2.getArguments();
if (args1.length != args2.length) {
buf = appendMsg(buf, "Number of args differs");
bailout = true;
}
else {
for (int ix = 0; ix < args1.length; ix++) {
if (!args1[ix].getName().equals(args2[ix].getName())) {
buf = appendMsg(buf, "Argument names differs");
break;
}
}
}
if (buf != null) {
report("Function attributes differ: " + buf, p1, f1, p2, f2);
if (bailout) {
return;
}
}
findRootDifferences(f1, f1.getReturnType(), f2, f2.getReturnType());
for (int ix = 0; ix < args1.length; ix++) {
findRootDifferences(f1, args1[ix].getDataType(), f2, args2[ix].getDataType());
}
}
private void findRootCompositeDifferences(DataType p1, Composite s1, DataType p2,
Composite s2) {
boolean bailout = false;
StringBuilder buf = null;
DataTypeComponent[] dtc1 = s1.getDefinedComponents();
DataTypeComponent[] dtc2 = s2.getDefinedComponents();
if (s1.getLength() != s2.getLength()) {
buf = appendMsg(buf, "Length differs (" + s1.getLength() + "/" + s2.getLength() + ")");
bailout = true;
}
if (dtc1.length != dtc2.length) {
buf = appendMsg(buf,
"Number of defined components differ (" + dtc1.length + "/" + dtc2.length + ")");
bailout = true;
}
if (s1.getAlignmentType() != s2.getAlignmentType()) {
buf = appendMsg(buf, "Alignment type differs (" + s1.getAlignmentType() + "/" +
s2.getAlignmentType() + ")");
}
if (s1.getPackingType() != s2.getPackingType()) {
buf = appendMsg(buf,
"Packing type differs (" + s1.getPackingType() + "/" + s2.getPackingType() + ")");
}
if (s1.getExplicitPackingValue() != s2.getExplicitPackingValue()) {
buf = appendMsg(buf, "Explicit pack value differs (" + s1.getExplicitPackingValue() +
"/" + s2.getExplicitPackingValue() + ")");
}
if (s1.getExplicitMinimumAlignment() != s2.getExplicitMinimumAlignment()) {
buf = appendMsg(buf, "Explicit min-alignment differs (" +
s1.getExplicitMinimumAlignment() + "/" + s2.getExplicitMinimumAlignment() + ")");
}
if (!s1.isPackingEnabled() && !s2.isPackingEnabled() && dtc1.length == dtc2.length) {
for (int ix = 0; ix < dtc1.length; ix++) {
if (dtc1[ix].getOffset() != dtc2[ix].getOffset() ||
dtc1[ix].getLength() != dtc2[ix].getLength()) {
buf = appendMsg(buf, "Component offsets/lengths vary)");
bailout = true;
break;
}
}
}
if (buf != null) {
report("Composite attributes differ: " + buf, p1, s1, p2, s2);
if (bailout) {
return;
}
}
for (int ix = 0; ix < dtc1.length; ix++) {
findRootDifferences(s1, dtc1[ix].getDataType(), s2, dtc2[ix].getDataType());
}
}
private StringBuilder appendMsg(StringBuilder buf, String msg) {
if (buf == null) {
buf = new StringBuilder();
}
if (buf.length() != 0) {
buf.append(", ");
}
buf.append(msg);
return buf;
}
private void report(String msg, DataType p1, DataType dt1, DataType p2, DataType dt2) {
hasReport = true;
println(msg + ":");
println(" " + dt1.getPathName() + " (" + dt1.getClass().getSimpleName() + ")");
if (p1 != null) {
println(" used by >> " + p1.getPathName());
}
println(" " + dt2.getPathName() + " (" + dt2.getClass().getSimpleName() + ")");
if (p2 != null) {
println(" used by >> " + p2.getPathName());
}
}
private int getPairHash(DataType dt1, DataType dt2) {
Long id1 = dtm.getID(dt1);
Long id2 = dtm.getID(dt2);
if (id1 < id2) {
// order IDs before hashing
Long tmp = id1;
id1 = id2;
id2 = tmp;
}
return Objects.hash(id1, id2);
}
}