mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Candidate release of source code.
This commit is contained in:
parent
db81e6b3b0
commit
79d8f164f8
12449 changed files with 2800756 additions and 16 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue