Candidate release of source code.

This commit is contained in:
Dan 2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View file

@ -0,0 +1,518 @@
/* ###
* 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.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.decompiler.parallel.*;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.DataTypeReference;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.util.Msg;
import ghidra.util.StringUtilities;
import ghidra.util.datastruct.SetAccumulator;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Implementation of {@link DataTypeReferenceFinder} that uses the Decompiler's output
* to find data type and composite field usage.
*/
public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinder {
public DecompilerDataTypeReferenceFinder() {
// for Extension Point loading
}
@Override
public void findReferences(Program program, DataType dataType,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
DecompilerDataTypeFinderQCallback qCallback =
new DecompilerDataTypeFinderQCallback(program, dataType, callback);
Set<Function> functions = filterFunctions(program, dataType, monitor);
try {
ParallelDecompiler.decompileFunctions(qCallback, program, functions, monitor);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // reset the flag
Msg.trace(this, "Interrupted while decompiling functions");
}
catch (Exception e) {
Msg.error(this, "Encountered an exception decompiling functions", e);
}
finally {
qCallback.dispose();
}
}
@Override
public void findReferences(Program program, Composite dataType, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
DecompilerDataTypeFinderQCallback qCallback =
new DecompilerDataTypeFinderQCallback(program, dataType, fieldName, callback);
Set<Function> functions = filterFunctions(program, dataType, monitor);
try {
ParallelDecompiler.decompileFunctions(qCallback, program, functions, monitor);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // reset the flag
Msg.debug(this, "Interrupted while decompiling functions");
}
catch (Exception e) {
Msg.error(this, "Encountered an exception decompiling functions", e);
}
finally {
qCallback.dispose();
}
}
private Set<Function> filterFunctions(Program program, DataType dt, TaskMonitor monitor)
throws CancelledException {
Set<DataType> types = new HashSet<>();
buildTypeLineage(dt, types);
Set<Function> results = new HashSet<>();
accumulateFunctionCallsToDefinedData(program, types, results, monitor);
Listing listing = program.getListing();
FunctionIterator it = listing.getFunctions(true);
for (Function f : it) {
monitor.checkCanceled();
if (results.contains(f)) {
continue;
}
if (usesAnyType(f, types)) {
results.add(f);
}
}
// note: do this here, so that don't cause the code above to skip processing these functions
Set<Function> callers = new HashSet<>();
for (Function f : results) {
monitor.checkCanceled();
Set<Function> callingFunctions = f.getCallingFunctions(monitor);
callers.addAll(callingFunctions);
}
results.addAll(callers);
return results;
}
private void accumulateFunctionCallsToDefinedData(Program program, Set<DataType> potentialTypes,
Set<Function> results, TaskMonitor monitor) throws CancelledException {
Listing listing = program.getListing();
AtomicInteger counter = new AtomicInteger();
SetAccumulator<LocationReference> accumulator = new SetAccumulator<>();
Predicate<Data> dataMatcher = data -> {
counter.incrementAndGet();
DataType dataType = data.getDataType();
boolean matches = potentialTypes.contains(dataType);
return matches;
};
ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher, null,
monitor);
for (LocationReference ref : accumulator) {
Address address = ref.getLocationOfUse();
Function f = listing.getFunctionContaining(address);
if (f != null) {
results.add(f);
}
}
}
/* Gets all types that are in the lineage of the given type */
private void buildTypeLineage(DataType sourceType, Set<DataType> types) {
if (types.contains(sourceType)) {
return; // already processed
}
// First, get this type, which could be a pointer, typedef or array...
gatherRelatedTypes(sourceType, types);
// Then, check the base type
DataType baseType = DataTypeUtils.getBaseDataType(sourceType);
if (types.contains(baseType)) {
return;
}
// We have a different type, should we search for it?
if (baseType instanceof BuiltInDataType) {
// When given a wrapper type (e.g., typedef) , ignore
// built-ins (e.g., int, byte, etc), as
// they will be of little value due to their volume in the program and the
// user *probably* did not intend to search for them. (Below we do not do
// this check, which allows the user to search directly for a
// built-in type, if they wish.)
return;
}
gatherRelatedTypes(baseType, types);
}
private void gatherRelatedTypes(DataType dt, Set<DataType> types) {
types.add(dt);
DataType[] parents = dt.getParents();
for (DataType parent : parents) {
buildTypeLineage(parent, types);
}
}
private boolean usesAnyType(Function f, Set<DataType> types) {
DataType returnType = f.getReturnType();
if (types.contains(returnType)) {
return true;
}
Variable[] variables = f.getAllVariables();
for (Variable v : variables) {
DataType paramType = v.getDataType();
if (types.contains(paramType) ||
types.contains(ReferenceUtils.getBaseDataType(paramType))) {
return true;
}
}
return false;
}
//==================================================================================================
// Classes
//==================================================================================================
private static class DecompilerDataTypeFinderQCallback
extends DecompilerCallback<List<DataTypeReference>> {
private Consumer<DataTypeReference> callback;
private DataType dataType;
private String fieldName;
/** Search for Data Type access only--no field usage */
DecompilerDataTypeFinderQCallback(Program program, DataType dataType,
Consumer<DataTypeReference> callback) {
this(program, dataType, null, callback);
}
/** Search for composite field access */
DecompilerDataTypeFinderQCallback(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback) {
super(program, new DecompilerConfigurer());
this.dataType = dataType;
this.fieldName = fieldName;
this.callback = callback;
}
@Override
public List<DataTypeReference> process(DecompileResults results, TaskMonitor monitor)
throws Exception {
Function function = results.getFunction();
if (function.isThunk()) {
return null;
}
DecompilerDataTypeFinder finder =
new DecompilerDataTypeFinder(results, function, dataType, fieldName);
List<DataTypeReference> refs = finder.findUsage();
refs.forEach(r -> callback.accept(r));
return refs;
}
}
private static class DecompilerConfigurer implements DecompileConfigurer {
@Override
public void configure(DecompInterface decompiler) {
decompiler.toggleCCode(true);
decompiler.toggleSyntaxTree(true);
decompiler.setSimplificationStyle("decompile");
DecompileOptions xmlOptions = new DecompileOptions();
xmlOptions.setDefaultTimeout(60);
decompiler.setOptions(xmlOptions);
}
}
/**
* Class to do the work of searching through the Decompiler's results for the desired
* data type access.
*/
private static class DecompilerDataTypeFinder {
private DecompileResults decompilation;
private Function function;
private DataType dataType;
private String fieldName;
DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType,
String fieldName) {
this.decompilation = results;
this.function = function;
this.dataType = dataType;
this.fieldName = fieldName;
}
List<DataTypeReference> findUsage() {
List<DataTypeReference> refs = new ArrayList<>();
searchDecompilation(refs);
return refs;
}
private void searchDecompilation(List<DataTypeReference> results) {
ClangTokenGroup tokens = decompilation.getCCodeMarkup();
// TODO delete this when the ticket settles down
// dumpTokens(tokens, 0);
// dumpTokenNames(tokens, 0);
if (tokens == null) {
// assume a bad function or the user cancelled the decompilation
Msg.trace(this, "Unable to get decompilation tokens for " + function.getName());
return;
}
List<DecompilerReference> variables = findVariableReferences(tokens);
variables.forEach(v -> matchUsage(v, results));
}
/** Finds any search input match in the given reference */
private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) {
reference.accumulateMatches(dataType, fieldName, results);
}
private List<DecompilerReference> findVariableReferences(ClangTokenGroup tokens) {
List<ClangLine> lines = DecompilerUtils.toLines(tokens);
List<DecompilerReference> result = new ArrayList<>();
for (ClangLine line : lines) {
findVariablesInLine(line, result);
}
return result;
}
private void findVariablesInLine(ClangLine line, List<DecompilerReference> results) {
// gather any casts until we can use them (the type they modify will follow)
List<DecompilerVariable> castsSoFar = new ArrayList<>();
VariableDR declaration = null;
VariableAccessDR access = null;
for (ClangToken token : line.getAllTokens()) {
if (token instanceof ClangTypeToken) {
if (token.Parent() instanceof ClangReturnType) {
results.add(new ReturnTypeDR(line, (ClangTypeToken) token));
}
else if (token.isVariableRef()) {
// Note: variable refs will get their variable in an upcoming token
if (isFunctionPrototype(token.Parent())) {
declaration = new ParameterDR(line, (ClangTypeToken) token);
}
else {
declaration = new LocalVariableDR(line, (ClangTypeToken) token);
}
results.add(declaration);
}
else {
// Assumption: this is a cast inside of a ClangStatement
// Assumption: there can be multiple casts concatenated
castsSoFar.add(new DecompilerVariableType(token));
}
}
else if (token instanceof ClangVariableToken) {
//
// Observations:
// 1) 'variableAccess' will be null if we are on a C statement that
// is a declaration (parameter or variable). In this case, 'ref' will
// be an instance of VariableDR.
// 2) 'variableAccess' will be null the first time a variable is used in
// a statement.
// 3) if 'variableAccess' is non-null, but already has a variable assigned,
// then this means the current ClangVariableToken represents a new
// variable access/usage.
//
if (declaration != null) {
declaration.setVariable((ClangVariableToken) token);
declaration = null;
}
else {
if (access == null || access.getVariable() != null) {
access = new VariableAccessDR(line);
results.add(access);
}
List<DecompilerVariable> casts = new ArrayList<>(castsSoFar);
access.setVariable((ClangVariableToken) token, casts);
castsSoFar.clear();
}
}
else if (token instanceof ClangFieldToken) {
List<DecompilerVariable> casts = new ArrayList<>(castsSoFar);
if (access == null) {
// Uh-oh. I've only seen this when line-wrapping is happening. In that
// case, try to get the last variable that we've seen and assume that
// is the variable to which this field belongs.
access = getLastAccess(results);
if (access == null) {
Msg.debug(this,
"Found a field access without a preceding " +
"variable for\n\tline: " + line + "\n\tfield: " + token +
"\n\tfunction: " + function);
continue;
}
}
access.addField((ClangFieldToken) token, casts);
castsSoFar.clear();
}
}
}
private VariableAccessDR getLastAccess(List<DecompilerReference> variables) {
// for now, assume that the last access will be the last item we added
if (variables.isEmpty()) {
return null; // shouldn't happen
}
DecompilerReference last = variables.get(variables.size() - 1);
if (last instanceof VariableAccessDR) {
return (VariableAccessDR) last;
}
return null; // shouldn't happen
}
private boolean isFunctionPrototype(ClangNode node) {
while (!(node instanceof ClangFuncProto) && node != null) {
node = node.Parent();
}
return node instanceof ClangFuncProto;
}
private void dumpTokens(ClangTokenGroup tokens, int depth) {
int n = tokens.numChildren();
for (int i = 0; i < n; i++) {
ClangNode child = tokens.Child(i);
doDumpTokens(child, depth);
}
}
private void doDumpTokens(ClangNode node, int depth) {
if (node instanceof ClangTokenGroup) {
dumpTokens((ClangTokenGroup) node, depth + 1);
return;
}
if (node instanceof ClangBreak) {
System.err.print("\n");
int tabs = depth * 4;
String asString = StringUtilities.pad("", ' ', tabs);
System.err.print(asString);
}
else {
String text = node.toString();
System.err.print(" '" + text + "' ");
}
ClangToken token = (ClangToken) node;
int n = node.numChildren();
for (int i = 0; i < n; i++) {
ClangNode child = token.Child(i);
doDumpTokens(child, depth + 1);
}
}
private void dumpTokenNames(ClangTokenGroup tokens, int depth) {
System.err.print(" '" + tokens.getClass().getSimpleName());
int n = tokens.numChildren();
for (int i = 0; i < n; i++) {
ClangNode child = tokens.Child(i);
doDumpTokenNames(child, depth);
}
}
private void doDumpTokenNames(ClangNode node, int depth) {
if (node instanceof ClangTokenGroup) {
dumpTokenNames((ClangTokenGroup) node, depth + 1);
return;
}
if (node instanceof ClangBreak) {
System.err.print("\n");
int tabs = depth * 4;
String asString = StringUtilities.pad("", ' ', tabs);
System.err.print(asString);
}
else {
System.err.print(
" '" + node.getClass().getSimpleName() + "' ['" + node.toString() + "'] ");
}
ClangToken token = (ClangToken) node;
int n = node.numChildren();
for (int i = 0; i < n; i++) {
ClangNode child = token.Child(i);
doDumpTokenNames(child, depth + 1);
}
}
}
}

View file

@ -0,0 +1,91 @@
/* ###
* 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.util.List;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
/**
* A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field
* of a variable, denoted by {@link ClangVariableToken}.
*/
public class DecompilerFieldAccess extends DecompilerVariable {
// for building on-the-fly
DecompilerFieldAccess() {
super(null);
}
DecompilerFieldAccess(ClangFieldToken field, List<DecompilerVariable> casts) {
super(field);
this.casts = casts;
}
@Override
public DataType getParentDataType() {
ClangFieldToken field = (ClangFieldToken) variable;
DataType dt = field.getDataType();
return dt;
}
@Override
public DataType getDataType() {
ClangFieldToken field = (ClangFieldToken) variable;
DataType dt = field.getDataType();
dt = getBaseType(dt);
if (!(dt instanceof Composite)) {
// can the dt be a pointer?
Msg.error(this, "Have a field for a type that is not a Composite type");
return dt;
}
int offset = field.getOffset();
Composite composite = (Composite) dt;
if (composite instanceof Structure) {
DataTypeComponent subType = ((Structure) composite).getComponentAt(offset);
if (subType != null) {
return subType.getDataType();
}
}
DataTypeComponent component = composite.getComponent(offset);
if (component == null) {
return null; // not sure what else to do
}
dt = component.getDataType();
return dt;
}
protected DataType getBaseType(DataType dt) {
if (dt instanceof Array) {
return getBaseType(((Array) dt).getDataType());
}
else if (dt instanceof Pointer) {
DataType baseDataType = ((Pointer) dt).getDataType();
if (baseDataType != null) {
return getBaseType(baseDataType);
}
}
else if (dt instanceof TypeDef) {
return getBaseType(((TypeDef) dt).getBaseDataType());
}
return dt;
}
}

View file

@ -0,0 +1,140 @@
/* ###
* 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.util.Arrays;
import java.util.List;
import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.DataTypeReference;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
import ghidra.util.StringUtilities;
/**
* A reference to a place as told by the Decompiler.
*/
public abstract class DecompilerReference {
protected ClangLine line;
protected ClangToken sourceToken;
protected DecompilerVariable variable;
protected DecompilerReference(ClangLine line, ClangToken token) {
this.line = line;
this.sourceToken = token;
// if token is null, then it will be filled-in later
if (token != null) {
this.variable = new DecompilerVariableType(token);
}
}
/**
* Scan this reference for any data type matches.
* <p>
* The <tt>fieldName</tt> is optional. If not included, then only data type matches will
* be sought. If it is included, then a match is only included when it is a reference
* to the given data type where that type is being accessed by the given field name.
*
* @param dt the data type to find
* @param fieldName the optional field name used to restrict matches.
* @param results the accumulator object into which will be placed any matches
*/
public abstract void accumulateMatches(DataType dt, String fieldName,
List<DataTypeReference> results);
public DecompilerVariable getVariable() {
return variable;
}
// Note: this is overridden when getting data types is more complicated (like when casting
// is involved).
public DataType getDataType() {
return variable.getDataType();
}
public Function getFunction() {
Function function = variable.getFunction();
return function;
}
public Address getAddress() {
return variable.getAddress();
}
public Address getAddress(DecompilerVariable var) {
return var.getAddress();
}
protected String getContext() {
String context = getContext(variable);
return context;
}
protected String getContext(DecompilerVariable var) {
String context = line.toDebugString(Arrays.asList(var.variable),
ReferenceUtils.CONTEXT_CALLOUT_START, ReferenceUtils.CONTEXT_CALLOUT_END);
return context;
}
// Note: using isEquivalent() allows different data types to match. I don't think we want
// that. This can be deleted in a few versions if we no longer need it.
public static boolean isEquivalent(DataType dt1, DataType dt2) {
DataType base1 = getBaseType(dt1);
DataType base2 = getBaseType(dt2);
return base1.isEquivalent(base2);
}
public static boolean isEqual(DataType dt1, DataType dt2) {
DataType base1 = getBaseType(dt1);
DataType base2 = getBaseType(dt2);
return base1.equals(base2);
}
public static DataType getBaseType(DataType dt) {
if (dt instanceof Array) {
return getBaseType(((Array) dt).getDataType());
}
else if (dt instanceof Pointer) {
DataType baseDataType = ((Pointer) dt).getDataType();
if (baseDataType != null) {
return getBaseType(baseDataType);
}
}
else if (dt instanceof TypeDef) {
DataType baseDataType = ((TypeDef) dt).getBaseDataType();
return getBaseType(baseDataType);
}
return dt;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tvariable: " + StringUtilities.toStringWithIndent(variable) + ",\n" +
"\tdata type: " + getDataType() + ",\n"+
"\tline " + getContext() + ",\n" +
"\tfunction: " + getFunction() + "\n" +
"}";
//@formatter:on
}
}

View file

@ -0,0 +1,173 @@
/* ###
* 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.util.ArrayList;
import java.util.List;
import ghidra.app.decompiler.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.*;
/**
* A base class that represents a variable from the decompiler. This is either a variable
* type or a variable with an optional field access.
*/
public abstract class DecompilerVariable {
protected List<DecompilerVariable> casts = new ArrayList<>();
protected ClangToken variable;
protected DecompilerVariable(ClangToken variable) {
this.variable = variable;
}
public List<DecompilerVariable> getCasts() {
return casts;
}
public DataType getParentDataType() {
// this is really here for subclasses
return getDataType();
}
public DataType getDataType() {
if (variable instanceof ClangTypeToken) {
return ((ClangTypeToken) variable).getDataType();
}
// Note: this is the icky part of the API. How to know from where to get the data type?
HighVariable highVariable = variable.getHighVariable();
if (highVariable != null) {
return highVariable.getDataType();
}
Varnode varnode = variable.getVarnode();
DataType dataType = getDataType(varnode);
if (dataType != null) {
return dataType;
}
// 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.
PcodeOp op = variable.getPcodeOp();
dataType = getInputDataType(op);
if (dataType instanceof PointerDataType) {
dataType = DecompilerReference.getBaseType(dataType);
if (dataType instanceof VoidDataType) {
// don't search for void
dataType = null;
}
}
if (dataType != null) {
return dataType;
}
// Finally, try the output varnode
dataType = getOutputDataType(op);
return dataType;
}
private DataType getInputDataType(PcodeOp op) {
if (op == null) {
return null;
}
Varnode[] inputs = op.getInputs();
if (inputs.length == 2) {
return inputs[0].getHigh().getDataType();
}
return null;
}
private DataType getOutputDataType(PcodeOp op) {
if (op == null) {
return null;
}
Varnode output = op.getOutput();
return output.getHigh().getDataType();
}
private DataType getDataType(Varnode varnode) {
if (varnode != null) {
HighVariable highVariable = varnode.getHigh();
if (highVariable != null) {
return highVariable.getDataType();
}
}
return null;
}
public ClangNode getParent() {
return variable.Parent();
}
public Function getFunction() {
ClangFunction clangFunction = variable.getClangFunction();
HighFunction highFunction = clangFunction.getHighFunction();
Function function = highFunction.getFunction();
return function;
}
public Address getAddress() {
Address minAddress = variable.getMinAddress();
if (minAddress != null) {
return minAddress;
}
// Note: some variables do not have an address, such as function parameters. In that
// case, we will walk backwards until we hit the function, using that address.
ClangNode parent = variable.Parent();
while (parent != null) {
if (parent instanceof ClangFunction) {
HighFunction highFunction = ((ClangFunction) parent).getHighFunction();
Function function = highFunction.getFunction();
Address entry = function.getEntryPoint();
return entry;
}
Address parentAddress = parent.getMinAddress();
if (parentAddress != null) {
return parentAddress;
}
parent = parent.Parent();
}
return null;
}
public String getName() {
String text = variable.getText();
return text;
}
@Override
public String toString() {
String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n";
//@formatter:off
return "{\n" +
castString +
"\tvariable: " + variable + ",\n" +
"}";
//@formatter:on
}
}

View file

@ -0,0 +1,36 @@
/* ###
* 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.util.List;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken;
/**
* A class to combine the various Decompiler tokens that are associated with data types.
*/
public class DecompilerVariableType extends DecompilerVariable {
DecompilerVariableType(ClangToken variable) {
super(variable);
}
DecompilerVariableType(ClangVariableToken token, List<DecompilerVariable> casts) {
super(token);
this.casts = casts;
}
}

View file

@ -0,0 +1,27 @@
/* ###
* 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 ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangTypeToken;
public class LocalVariableDR extends VariableDR {
protected LocalVariableDR(ClangLine line, ClangTypeToken type) {
super(line, type);
}
}

View file

@ -0,0 +1,27 @@
/* ###
* 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 ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangTypeToken;
public class ParameterDR extends VariableDR {
ParameterDR(ClangLine line, ClangTypeToken token) {
super(line, token);
}
}

View file

@ -0,0 +1,49 @@
/* ###
* 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.util.List;
import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangTypeToken;
import ghidra.app.services.DataTypeReference;
import ghidra.program.model.data.DataType;
public class ReturnTypeDR extends DecompilerReference {
ReturnTypeDR(ClangLine line, ClangTypeToken type) {
super(line, type);
}
@Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
if (fieldName != null) {
// Return Types do not have any field usage
return;
}
DataType myDt = getDataType();
if (myDt == null) {
return;
}
if (isEqual(dt, myDt)) {
results.add(
new DataTypeReference(myDt, null, getFunction(), getAddress(), getContext()));
}
}
}

View file

@ -0,0 +1,274 @@
/* ###
* 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.util.ArrayList;
import java.util.List;
import ghidra.app.decompiler.*;
import ghidra.app.services.DataTypeReference;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighGlobal;
import ghidra.program.model.pcode.HighVariable;
import ghidra.util.StringUtilities;
import ghidra.util.exception.AssertException;
/**
* A class that describes Decompiler variables and the fields that they may access.
*/
public class VariableAccessDR extends DecompilerReference {
private List<DecompilerFieldAccess> fields = new ArrayList<>();
protected VariableAccessDR(ClangLine line) {
super(line, null /* This class does not always have a 'type' token */);
}
void setVariable(ClangVariableToken token, List<DecompilerVariable> casts) {
if (variable != null) {
throw new AssertException("Decompiler variable is already set for this access");
}
variable = new DecompilerVariableType(token, casts);
}
void addField(ClangFieldToken token, List<DecompilerVariable> fieldCasts) {
DecompilerFieldAccess field = new DecompilerFieldAccess(token, fieldCasts);
fields.add(field);
}
@Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
if (fields.isEmpty()) {
DecompilerVariable var = getMatch(dt, fieldName, variable, null);
if (var != null) {
DataTypeReference ref = createReference(var);
results.add(ref);
}
return;
}
//
// Walk each pair of type and variables in order to see if any of them match our
// criteria
//
DecompilerVariable start = variable;
for (DecompilerVariable field : fields) {
DecompilerVariable next = field;
DecompilerVariable var = getMatch(dt, fieldName, start, next);
if (var != null) {
DataTypeReference ref = createReference(var, next);
results.add(ref);
}
start = next;
}
//
// Handle the last variable by itself (for the case where we are matching just on the
// type, with no field name)
//
if (fieldName != null) {
return;
}
DecompilerVariable var = getMatch(dt, null, start, null);
if (var != null) {
DataTypeReference ref = createReference(var);
results.add(ref);
}
}
private DecompilerVariable getMatch(DataType dt, String fieldName, DecompilerVariable var,
DecompilerVariable potentialField) {
// Note: for now, I ignore the precedence of casting; if any cast type is a match, then
// signal hooray
boolean searchForField = fieldName != null;
DecompilerVariable fieldVar = searchForField ? potentialField : null;
DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar);
if (match == null) {
// wrong type, nothing to do
return null;
}
// Matches on the type, does the field match?
if (fieldName == null) {
return match; // no field to match
}
if (potentialField == null) {
return null; // we seek a field, but there is none
}
String name = potentialField.getName();
if (fieldName.equals(name)) {
return match;
}
return null;
}
private DecompilerVariable getMatchingVarialbe(DataType dt, DecompilerVariable var,
DecompilerVariable potentialField) {
List<DecompilerVariable> castVariables = var.getCasts();
for (DecompilerVariable cast : castVariables) {
if (matchesType(cast, dt)) {
return cast;
}
}
if (matchesType(var, dt)) {
return var;
}
//
// Unusual Code Alert!
// It is a bit odd to check the field when you are looking for the type that contains
// the field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the
// data type of the thing that contains it. So, if you have:
// foo.bar
// then the 'bar' field will have a data type of Foo. Unfortunately, this is not
// always the case. For now, when the variable is global, we need to check the field
// Sad face emoji.
//
HighVariable highVariable = var.variable.getHighVariable();
if (highVariable instanceof HighGlobal) {
if (matchesParentType(potentialField, dt)) {
return potentialField;
}
}
return null;
}
private boolean matchesParentType(DecompilerVariable var, DataType dt) {
if (var == null) {
return false;
}
DataType varType = var.getParentDataType();
boolean matches = isEqual(varType, dt);
return matches;
}
private boolean matchesType(DecompilerVariable var, DataType dt) {
if (var == null) {
return false;
}
DataType varType = var.getDataType();
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
return false;
}
boolean matches = isEqual(varType, dt);
return matches;
}
private DataTypeReference createReference(DecompilerVariable var) {
DataType dataType = var.getDataType();
String context = getContext(var);
Function function = var.getFunction();
Address address = getAddress(var);
return new DataTypeReference(dataType, null, function, address, context);
}
private DataTypeReference createReference(DecompilerVariable var, DecompilerVariable field) {
DataType dataType = var.getDataType();
String context = getContext(var);
Function function = var.getFunction();
Address address = getAddress(var);
return new DataTypeReference(dataType, field.getName(), function, address, context);
}
@Override
protected String getContext(DecompilerVariable var) {
DecompilerVariable field = findFieldFor(var);
String context = super.getContext(field);
return context;
}
private DecompilerVariable findFieldFor(DecompilerVariable var) {
//
// Unusual Code Alert!
//
// The fact that we need to locate the given variable is a bit odd. But, elsewhere in
// this file we figured out which variable (out of the casts, the variable and the
// accesses) is the type we seek. So, now we have a variable, but we don't know
// if it is the field or if one of the accesses is the field. So, this method walks
// all variables in order to find the given variable, then returns the next variable, as
// this represents the field in which we are interested.
//
List<DecompilerVariable> allVars = getAllVariablesInOrder();
int varIndex = allVars.indexOf(var);
if (varIndex == -1) {
// this shouldn't happen; die gracefully, just in case
throw new AssertException("Cannot find a field for variable " + var);
}
// assume 'var' is the parent of the field we seek (it may actually be the field itself)
int fieldIndex = varIndex + 1;
if (fieldIndex == allVars.size()) {
// ...it must be the variable and the field
fieldIndex = allVars.size() - 1;
}
DecompilerVariable field = allVars.get(fieldIndex);
return field;
}
private List<DecompilerVariable> getAllVariablesInOrder() {
List<DecompilerVariable> allVars = new ArrayList<>();
getAllVariableTypes(variable, allVars);
for (DecompilerVariable field : fields) {
getAllVariableTypes(field, allVars);
}
return allVars;
}
private void getAllVariableTypes(DecompilerVariable var, List<DecompilerVariable> result) {
List<DecompilerVariable> casts = var.getCasts();
result.addAll(casts);
result.add(var);
}
@Override
public String toString() {
String subFieldsString = fields.isEmpty() ? ""
: "\tsub fields: " + StringUtilities.toStringWithIndent(fields) + ",\n";
//@formatter:off
return "{\n" +
"\tvariable: " + StringUtilities.toStringWithIndent(variable) + ",\n" +
"\tdata type: " + getDataType() + ",\n"+
subFieldsString +
"\tline " + getContext() + ",\n" +
"\tfunction: " + getFunction() + "\n" +
"}";
//@formatter:on
}
}

View file

@ -0,0 +1,81 @@
/* ###
* 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.util.List;
import ghidra.app.decompiler.*;
import ghidra.app.services.DataTypeReference;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Function;
import ghidra.util.exception.AssertException;
/**
* Represents a type declaration and variable pair.
*/
public abstract class VariableDR extends DecompilerReference {
protected DecompilerVariable declaration;
protected VariableDR(ClangLine line, ClangTypeToken type) {
super(line, type);
declaration = new DecompilerVariableType(type);
}
void setVariable(ClangVariableToken token) {
variable = new DecompilerVariableType(token);
}
public DecompilerVariable getDeclaration() {
return declaration;
}
@Override
public DecompilerVariable getVariable() {
return variable;
}
@Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
if (variable == null) {
// This implies our API was misused in that a variable was never set after creation
throw new AssertException("Decompiler variable declaration is missing a name");
}
DataType dataType = getDataType();
if (!isEqual(dataType, dt)) {
// wrong type, nothing to do
return;
}
String context = getContext();
Function function = getFunction();
Address address = getAddress();
if (fieldName == null) {
// no field to check, a match on the the type is good enough
results.add(new DataTypeReference(dataType, null, getFunction(), address, context));
return;
}
String name = variable.getName();
if (name.equals(fieldName)) {
results.add(new DataTypeReference(dataType, fieldName, function, address, context));
}
}
}