mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Test debug
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,17 +15,13 @@
|
||||
*/
|
||||
package ghidra.app.extension.datatype.finder;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.collections4.IterableUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import generic.io.NullPrintWriter;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||
import ghidra.app.decompiler.parallel.*;
|
||||
@@ -37,7 +33,8 @@ import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.BuiltInDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.StringUtilities;
|
||||
import ghidra.util.datastruct.SetAccumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -287,8 +284,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
private DataType dataType;
|
||||
private FieldMatcher fieldMatcher;
|
||||
|
||||
private ByteArrayOutputStream debugBytes = new ByteArrayOutputStream();
|
||||
private PrintWriter debugWriter = new PrintWriter(debugBytes);
|
||||
private String dbgPrefix;
|
||||
|
||||
DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType,
|
||||
FieldMatcher fieldMatcher) {
|
||||
@@ -297,12 +293,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
this.dataType = dataType;
|
||||
this.fieldMatcher = fieldMatcher;
|
||||
|
||||
if (SystemUtilities.isInTestingMode()) {
|
||||
debugWriter = new PrintWriter(debugBytes);
|
||||
}
|
||||
else {
|
||||
debugWriter = new NullPrintWriter();
|
||||
}
|
||||
this.dbgPrefix = "f: " + function + "\n\t";
|
||||
}
|
||||
|
||||
List<DataTypeReference> findUsage() {
|
||||
@@ -326,21 +317,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
return;
|
||||
}
|
||||
|
||||
debugWriter.println("f: " + function + "\n\tchecking vars...");
|
||||
DtrfDbg.println(dbgPrefix + "checking vars...");
|
||||
List<DecompilerReference> variables = findVariableReferences(tokens);
|
||||
debugWriter.println("f: " + function + "\n\t...done checking");
|
||||
|
||||
debugWriter.flush();
|
||||
String output = debugBytes.toString();
|
||||
if (!StringUtils.isBlank(output)) {
|
||||
Msg.debug(this, "Final Debug:\n" + output);
|
||||
}
|
||||
DtrfDbg.println(dbgPrefix + "DONE searching decompilation\nMatching results");
|
||||
|
||||
variables.forEach(v -> matchUsage(v, results));
|
||||
}
|
||||
|
||||
/** Finds any search input match in the given reference */
|
||||
private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) {
|
||||
DtrfDbg.println("Checking " + reference);
|
||||
reference.accumulateMatches(dataType, fieldMatcher, results);
|
||||
}
|
||||
|
||||
@@ -388,12 +374,12 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
VariableAccessDR access = null;
|
||||
for (ClangToken token : filteredTokens) {
|
||||
|
||||
debugWriter.println("f: " + function + "\n\tchecking token: " + token);
|
||||
DtrfDbg.println(dbgPrefix + "checking token: " + token);
|
||||
|
||||
if (token instanceof ClangTypeToken) {
|
||||
|
||||
if (token.Parent() instanceof ClangReturnType) {
|
||||
debugWriter.println("f: " + function + "\n\t\treturn type: " + line);
|
||||
DtrfDbg.println(dbgPrefix + "\treturn type: " + line);
|
||||
|
||||
results.add(new ReturnTypeDR(line, (ClangTypeToken) token));
|
||||
}
|
||||
@@ -401,13 +387,13 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
// Note: variable refs will get their variable in an upcoming token
|
||||
if (isFunctionPrototype(token.Parent())) {
|
||||
|
||||
debugWriter.println("f: " + function + "\n\t\tparameter: " + line);
|
||||
DtrfDbg.println(dbgPrefix + "\tparameter: " + line);
|
||||
|
||||
declaration = new ParameterDR(line, (ClangTypeToken) token);
|
||||
}
|
||||
else {
|
||||
|
||||
debugWriter.println("f: " + function + "\n\t\tlocal var: " + line);
|
||||
DtrfDbg.println(dbgPrefix + "\tlocal var: " + line);
|
||||
|
||||
declaration = new LocalVariableDR(line, (ClangTypeToken) token);
|
||||
}
|
||||
@@ -416,7 +402,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
}
|
||||
else {
|
||||
|
||||
debugWriter.println("f: " + function + "\n\t\tadding a cast");
|
||||
DtrfDbg.println(dbgPrefix + "\tadding a cast");
|
||||
|
||||
// Assumption: this is a cast inside of a ClangStatement
|
||||
// Assumption: there can be multiple casts concatenated
|
||||
@@ -438,8 +424,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
//
|
||||
if (declaration != null) {
|
||||
|
||||
debugWriter.println(
|
||||
"f: " + function + "\n\t\thave declaration - " + declaration);
|
||||
DtrfDbg.println(dbgPrefix + "\thave declaration - " + declaration);
|
||||
|
||||
declaration.setVariable((ClangVariableToken) token);
|
||||
declaration = null;
|
||||
@@ -447,8 +432,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
else {
|
||||
if (access == null || access.getVariable() != null) {
|
||||
|
||||
debugWriter.println(
|
||||
"f: " + function + "\n\t\tcreating variable access: " + line);
|
||||
DtrfDbg.println(dbgPrefix + "\tcreating variable access: " + line);
|
||||
|
||||
access = new VariableAccessDR(line);
|
||||
results.add(access);
|
||||
@@ -481,8 +465,8 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||
ClangFieldToken field = (ClangFieldToken) token;
|
||||
if (typesDoNotMatch(access, field)) {
|
||||
|
||||
debugWriter.println("f: " + function +
|
||||
"\n\t\tcreating an anonymous variable access: " + line);
|
||||
DtrfDbg.println(
|
||||
dbgPrefix + "\tcreating an anonymous variable access: " + line);
|
||||
|
||||
// this can happen when a field is used anonymously, such as directly
|
||||
// after a nested array index operation
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.extension.datatype.finder;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import generic.io.NullPrintWriter;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A package utility class to allow for tests to selectively enable debug output. This class is
|
||||
* used instead of generic logging with the intent that this class will be removed when the bug(s)
|
||||
* are fixed.
|
||||
*/
|
||||
class DtrfDbg {
|
||||
|
||||
private static ByteArrayOutputStream debugBytes;
|
||||
private static PrintWriter debugWriter = new NullPrintWriter();
|
||||
|
||||
private static List<String> clientFilters = new ArrayList<>();
|
||||
|
||||
DtrfDbg() {
|
||||
// static class
|
||||
}
|
||||
|
||||
static void enable() {
|
||||
debugBytes = new ByteArrayOutputStream();
|
||||
debugWriter = new PrintWriter(debugBytes);
|
||||
}
|
||||
|
||||
private static void close() {
|
||||
debugWriter.close();
|
||||
debugWriter = new NullPrintWriter();
|
||||
}
|
||||
|
||||
static void disable(boolean write) {
|
||||
|
||||
if (!write) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
debugWriter.flush();
|
||||
String output = debugBytes.toString();
|
||||
if (!StringUtils.isBlank(output)) {
|
||||
Msg.debug(DtrfDbg.class, "\n\nFinal Debug:\n" + output);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets filters that will be checked against the {@code toString()} of each client. The
|
||||
* filtering is a case-sensitive 'contains' check.
|
||||
* @param filters the text
|
||||
*/
|
||||
static void setClientToStringFilters(String... filters) {
|
||||
clientFilters.clear();
|
||||
clientFilters.addAll(Arrays.asList(filters));
|
||||
}
|
||||
|
||||
static void println(String s) {
|
||||
debugWriter.println(s);
|
||||
}
|
||||
|
||||
static void println(Object client, String s) {
|
||||
if (!passesFilter(client)) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugWriter.println(s);
|
||||
}
|
||||
|
||||
private static boolean passesFilter(Object client) {
|
||||
if (client == null || clientFilters.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String asString = client.toString();
|
||||
return clientFilters.stream().anyMatch(s -> asString.contains(s));
|
||||
}
|
||||
}
|
||||
@@ -106,22 +106,28 @@ public class VariableAccessDR extends DecompilerReference {
|
||||
private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher,
|
||||
DecompilerVariable var, DecompilerVariable potentialField) {
|
||||
|
||||
String indent = "\t\t";
|
||||
|
||||
// Note: for now, I ignore the precedence of casting; if any cast type is a match, then
|
||||
// signal hooray
|
||||
boolean searchForField = !fieldMatcher.isIgnored();
|
||||
DecompilerVariable fieldVar = searchForField ? potentialField : null;
|
||||
DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar);
|
||||
if (match == null) {
|
||||
DtrfDbg.println(this, indent + "NO MATCHING VARIABLE");
|
||||
return null; // wrong type, nothing to do
|
||||
}
|
||||
|
||||
// Matches on the type, does the field match?
|
||||
if (fieldMatcher.isIgnored()) {
|
||||
DtrfDbg.println(this, indent + "field macher is ignored; returning match");
|
||||
return match; // no field to match
|
||||
}
|
||||
|
||||
if (potentialField == null) {
|
||||
|
||||
DtrfDbg.println(this, indent + "No potential field to match; name / offset match?");
|
||||
|
||||
// check for the case where we have not been passed a 'potential field', but the given
|
||||
// 'var' is itself may be the field we seek, such as in an if statement like this:
|
||||
// if (color == RED)
|
||||
@@ -129,34 +135,52 @@ public class VariableAccessDR extends DecompilerReference {
|
||||
String name = var.getName();
|
||||
int offset = var.getOffset();
|
||||
if (fieldMatcher.matches(name, offset)) {
|
||||
DtrfDbg.println(this, indent + "\tfield matcher matched on variable: " + var);
|
||||
return var;
|
||||
}
|
||||
|
||||
DtrfDbg.println(this, indent + "\tNO FIELD MATCHER MATCH");
|
||||
return null; // we seek a field, but there is none
|
||||
}
|
||||
|
||||
DtrfDbg.println(this, indent + "Checking 'potential field' match...");
|
||||
|
||||
String name = potentialField.getName();
|
||||
int offset = potentialField.getOffset();
|
||||
if (fieldMatcher.matches(name, offset)) {
|
||||
DtrfDbg.println(this, indent + "\tMATCHED");
|
||||
return match;
|
||||
}
|
||||
DtrfDbg.println(this, indent + "\tNO MATCH");
|
||||
return null;
|
||||
}
|
||||
|
||||
private DecompilerVariable getMatchingVarialbe(DataType dt, DecompilerVariable var,
|
||||
DecompilerVariable potentialField) {
|
||||
|
||||
String indent = "\t\t\t";
|
||||
|
||||
DtrfDbg.println(this, indent + "Checking for matching variable; any casts?");
|
||||
List<DecompilerVariable> castVariables = var.getCasts();
|
||||
for (DecompilerVariable cast : castVariables) {
|
||||
if (matchesType(cast, dt)) {
|
||||
DtrfDbg.println(this, indent + "MATCHED cast: " + cast);
|
||||
return cast;
|
||||
}
|
||||
}
|
||||
|
||||
String dtString = dt == null ? "null" : dt.toString();
|
||||
DtrfDbg.println(this,
|
||||
indent + "No matched casts; checking type against var:\n" +
|
||||
StringUtilities.indentLines("type: " + dtString, indent + "\t") + "\n" +
|
||||
StringUtilities.indentLines("var: " + var.toString(), indent + "\t"));
|
||||
if (matchesType(var, dt)) {
|
||||
DtrfDbg.println(this, indent + "MATCHED type: ");
|
||||
return var;
|
||||
}
|
||||
|
||||
DtrfDbg.println(this, indent + "Type did not match; checking High Variable: ");
|
||||
|
||||
//
|
||||
// Unusual Code Alert!
|
||||
// It is a bit odd to check the field when you are looking for the type that contains the
|
||||
@@ -170,10 +194,12 @@ public class VariableAccessDR extends DecompilerReference {
|
||||
HighVariable highVariable = var.variable.getHighVariable();
|
||||
if (highVariable instanceof HighGlobal) {
|
||||
if (matchesParentType(potentialField, dt)) {
|
||||
DtrfDbg.println(this, indent + "MATCHED on parent type: " + dt);
|
||||
return potentialField;
|
||||
}
|
||||
}
|
||||
|
||||
DtrfDbg.println(this, indent + "NOT MATCHED");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -188,7 +214,11 @@ public class VariableAccessDR extends DecompilerReference {
|
||||
}
|
||||
|
||||
private boolean matchesType(DecompilerVariable var, DataType dt) {
|
||||
|
||||
String indent = "\t\t\t\t";
|
||||
|
||||
if (var == null) {
|
||||
DtrfDbg.println(this, indent + "Types Match? no variable to check");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -196,6 +226,7 @@ public class VariableAccessDR extends DecompilerReference {
|
||||
if (varType == null) {
|
||||
// it seems odd to me that there is no type, but I have seen this in the case
|
||||
// statement of a switch
|
||||
DtrfDbg.println(this, indent + "ypes Match? no variable TYPE to check");
|
||||
return false;
|
||||
}
|
||||
boolean matches = isEqual(varType, dt);
|
||||
|
||||
Reference in New Issue
Block a user