mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Multi-merge functionality
This commit is contained in:
parent
cbcfaf54fa
commit
6c6d5f2f1b
9 changed files with 256 additions and 61 deletions
|
@ -216,9 +216,11 @@ public class HighFunctionDBUtil {
|
|||
}
|
||||
String name = sym.getName();
|
||||
try {
|
||||
Variable var = clearConflictingLocalVariables(sym);
|
||||
Variable var =
|
||||
clearConflictingLocalVariables(function, sym.getStorage(), sym.getPCAddress());
|
||||
if (var == null) {
|
||||
var = createLocalVariable(sym, null, null, source);
|
||||
var = createLocalVariable(function, sym.getDataType(), sym.getStorage(),
|
||||
sym.getPCAddress(), source);
|
||||
if (name != null) {
|
||||
var.setName(name, source);
|
||||
}
|
||||
|
@ -237,26 +239,24 @@ public class HighFunctionDBUtil {
|
|||
|
||||
/**
|
||||
* Create a local DB variable with a default name. Storage and data-type for the variable
|
||||
* can be provided explicitly, or they can be taken from a decompiler symbol.
|
||||
* @param symbol is the decompiler symbol
|
||||
* @param dt is the given data-type or null (to use the symbol's data-type)
|
||||
* @param storage is the given storage or null (to use the symbol's storage)
|
||||
* @param source is the desired SourceType of the new variable
|
||||
* are provided explicitly.
|
||||
* @param function is the function owning the new variable
|
||||
* @param dt is the given data-type
|
||||
* @param storage is the given storage
|
||||
* @param pcAddr is point where the variable is instantiated or null
|
||||
* @param source is the source type of the new variable
|
||||
* @return the new local variable
|
||||
* @throws InvalidInputException is a valid variable can't be created
|
||||
*/
|
||||
private static Variable createLocalVariable(HighSymbol symbol, DataType dt,
|
||||
VariableStorage storage, SourceType source) throws InvalidInputException {
|
||||
Function function = symbol.getHighFunction().getFunction();
|
||||
private static Variable createLocalVariable(Function function, DataType dt,
|
||||
VariableStorage storage, Address pcAddr, SourceType source)
|
||||
throws InvalidInputException {
|
||||
Program program = function.getProgram();
|
||||
if (storage == null || storage.isUniqueStorage()) {
|
||||
storage = symbol.getStorage();
|
||||
int firstUseOffset = 0;
|
||||
if (pcAddr != null) {
|
||||
firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint());
|
||||
}
|
||||
if (dt == null) {
|
||||
dt = symbol.getDataType();
|
||||
}
|
||||
Variable var =
|
||||
new LocalVariableImpl(null, symbol.getFirstUseOffset(), dt, storage, program);
|
||||
Variable var = new LocalVariableImpl(null, firstUseOffset, dt, storage, program);
|
||||
try {
|
||||
var = function.addLocalVariable(var, source);
|
||||
}
|
||||
|
@ -266,8 +266,8 @@ public class HighFunctionDBUtil {
|
|||
|
||||
Register reg = var.getRegister();
|
||||
if (reg != null) {
|
||||
program.getReferenceManager().addRegisterReference(symbol.getPCAddress(), -1, reg,
|
||||
RefType.WRITE, source);
|
||||
program.getReferenceManager().addRegisterReference(pcAddr, -1, reg, RefType.WRITE,
|
||||
source);
|
||||
}
|
||||
|
||||
return var;
|
||||
|
@ -305,33 +305,84 @@ public class HighFunctionDBUtil {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a particular seed Variable, find the set of local Variables that are intended to be
|
||||
* merged containing that seed. The result will be an array with at least the seed variable in it.
|
||||
* @param function is the function containing the local variables
|
||||
* @param seed is the seed local variable
|
||||
* @return an array of all Variables intended to be merged.
|
||||
*/
|
||||
private static Variable[] gatherMergeSet(Function function, Variable seed) {
|
||||
TreeMap<String, Variable> nameMap = new TreeMap<String, Variable>();
|
||||
for (Variable var : function.getAllVariables()) {
|
||||
nameMap.put(var.getName(), var);
|
||||
}
|
||||
String baseName = seed.getName();
|
||||
int pos = baseName.lastIndexOf('$');
|
||||
if (pos >= 0) {
|
||||
baseName = baseName.substring(0, pos);
|
||||
}
|
||||
DataType dataType = seed.getDataType();
|
||||
Variable currentVar = nameMap.get(baseName);
|
||||
int index = 0;
|
||||
boolean sawSeed = false;
|
||||
ArrayList<Variable> mergeArray = new ArrayList<Variable>();
|
||||
for (;;) {
|
||||
if (currentVar == null) {
|
||||
break;
|
||||
}
|
||||
if (!currentVar.getDataType().equals(dataType)) {
|
||||
break;
|
||||
}
|
||||
if (index != 0 && currentVar instanceof Parameter) {
|
||||
break;
|
||||
}
|
||||
if (index != 0 && currentVar.hasStackStorage()) {
|
||||
break;
|
||||
}
|
||||
if (currentVar == seed) {
|
||||
sawSeed = true;
|
||||
}
|
||||
mergeArray.add(currentVar);
|
||||
index += 1;
|
||||
String newName = baseName + '$' + Integer.toString(index);
|
||||
currentVar = nameMap.get(newName);
|
||||
}
|
||||
Variable[] res;
|
||||
if (!sawSeed) {
|
||||
res = new Variable[1];
|
||||
res[0] = seed;
|
||||
}
|
||||
else {
|
||||
res = new Variable[mergeArray.size()];
|
||||
mergeArray.toArray(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Low-level routine for clearing any variables in the
|
||||
* database which conflict with this variable and return
|
||||
* one of them for re-use. The returned variable still
|
||||
* exists within the function at the same first-use-offset.
|
||||
* @param function is the function containing the local variables
|
||||
* @param storage is the storage area to clear
|
||||
* @param pcAddr is the point of use
|
||||
* @return existing variable with identical storage and first-use offset or null
|
||||
*/
|
||||
private static Variable clearConflictingLocalVariables(HighSymbol local) {
|
||||
private static Variable clearConflictingLocalVariables(Function function,
|
||||
VariableStorage storage, Address pcAddr) {
|
||||
|
||||
SymbolEntry entry = local.getFirstWholeMap();
|
||||
if (entry instanceof MappedEntry) {
|
||||
if (local.isParameter()) { // Don't clear parameters
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int firstUseOffset = 0;
|
||||
if (pcAddr != null) {
|
||||
firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint());
|
||||
}
|
||||
if (storage.isHashStorage()) {
|
||||
|
||||
HighFunction highFunction = local.getHighFunction();
|
||||
Function func = highFunction.getFunction();
|
||||
|
||||
VariableStorage storage = local.getStorage();
|
||||
int firstUseOffset = local.getFirstUseOffset();
|
||||
if (entry instanceof DynamicEntry) {
|
||||
|
||||
DynamicEntry dynamicEntry = (DynamicEntry) entry;
|
||||
for (Variable ul : func.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) {
|
||||
long hashVal = storage.getFirstVarnode().getOffset();
|
||||
for (Variable ul : function.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) {
|
||||
// Note: assumes there is only one hash method used for unique locals
|
||||
if (ul.getFirstStorageVarnode().getOffset() == dynamicEntry.getHash()) {
|
||||
if (ul.getFirstStorageVarnode().getOffset() == hashVal) {
|
||||
return ul;
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +390,7 @@ public class HighFunctionDBUtil {
|
|||
}
|
||||
|
||||
Variable matchingVariable = null;
|
||||
for (Variable otherVar : func.getLocalVariables()) {
|
||||
for (Variable otherVar : function.getLocalVariables()) {
|
||||
if (otherVar.getFirstUseOffset() != firstUseOffset) {
|
||||
// other than parameters we will have a hard time identifying
|
||||
// local variable conflicts due to differences in scope (i.e., first-use)
|
||||
|
@ -353,7 +404,7 @@ public class HighFunctionDBUtil {
|
|||
matchingVariable = otherVar;
|
||||
continue;
|
||||
}
|
||||
func.removeVariable(otherVar);
|
||||
function.removeVariable(otherVar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -453,22 +504,31 @@ public class HighFunctionDBUtil {
|
|||
}
|
||||
}
|
||||
else if (!highSymbol.isGlobal()) {
|
||||
Variable var;
|
||||
Variable[] varList = null;
|
||||
VariableStorage storage = highSymbol.getStorage();
|
||||
Address pcAddr = highSymbol.getPCAddress();
|
||||
HighVariable tmpHigh = highSymbol.getHighVariable();
|
||||
if (!storage.isHashStorage() && tmpHigh != null &&
|
||||
tmpHigh.requiresDynamicStorage()) {
|
||||
storage =
|
||||
DynamicEntry.buildDynamicStorage(tmpHigh.getRepresentative(), highFunction);
|
||||
var = null;
|
||||
}
|
||||
else {
|
||||
var = clearConflictingLocalVariables(highSymbol);
|
||||
Variable var = clearConflictingLocalVariables(function, storage, pcAddr);
|
||||
if (var != null) {
|
||||
if (!resized) {
|
||||
varList = gatherMergeSet(function, var); // Cannot resize a whole multi-merge
|
||||
}
|
||||
else {
|
||||
varList = new Variable[1];
|
||||
varList[0] = var;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean usesHashStorage = storage.isHashStorage();
|
||||
if (dataType == null) {
|
||||
if (var != null) {
|
||||
dataType = var.getDataType(); // Use preexisting datatype if it fits in desired storage
|
||||
if (varList != null) {
|
||||
dataType = varList[0].getDataType(); // Use preexisting datatype if it fits in desired storage
|
||||
}
|
||||
else {
|
||||
dataType = Undefined.getUndefinedDataType(highSymbol.getSize());
|
||||
|
@ -484,19 +544,34 @@ public class HighFunctionDBUtil {
|
|||
storage = VariableUtilities.resizeStorage(storage, dataType, true, function);
|
||||
}
|
||||
|
||||
if (var == null) {
|
||||
var = createLocalVariable(highSymbol, dataType, storage, source);
|
||||
if (varList == null) {
|
||||
Variable var = createLocalVariable(function, dataType, storage, pcAddr, source);
|
||||
varList = new Variable[1];
|
||||
varList[0] = var;
|
||||
}
|
||||
else if (resized) {
|
||||
// Set resized data-type on existing Variable
|
||||
varList[0].setDataType(dataType, storage, true, source);
|
||||
}
|
||||
else {
|
||||
// fixup reused variable
|
||||
var.setDataType(dataType, storage, true, source);
|
||||
// Set data-type on existing merge set
|
||||
for (Variable var : varList) {
|
||||
var.setDataType(dataType, source);
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
name = highSymbol.getName(); // must update name if not specified
|
||||
}
|
||||
Variable renameVar = null;
|
||||
try {
|
||||
// must set/correct name
|
||||
var.setName(name, source);
|
||||
int index = 0;
|
||||
String curName = name;
|
||||
for (Variable var : varList) {
|
||||
renameVar = var;
|
||||
var.setName(curName, source);
|
||||
index += 1;
|
||||
curName = name + '$' + Integer.toString(index);
|
||||
}
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
if (isRename) {
|
||||
|
@ -507,7 +582,7 @@ public class HighFunctionDBUtil {
|
|||
Msg.error(HighFunctionDBUtil.class,
|
||||
"Name conflict while naming local variable: " + function.getName() + ":" +
|
||||
name);
|
||||
var.setName(null, SourceType.DEFAULT);
|
||||
renameVar.setName(null, SourceType.DEFAULT);
|
||||
}
|
||||
catch (DuplicateNameException e1) {
|
||||
throw new AssertException("Unexpected exception with default name", e);
|
||||
|
|
|
@ -75,11 +75,85 @@ public class LocalSymbolMap {
|
|||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct and return a map from a HighSymbol's name to the HighSymbol object
|
||||
* @return the new name to symbol map
|
||||
*/
|
||||
public Map<String, HighSymbol> getNameToSymbolMap() {
|
||||
Map<String, HighSymbol> newMap = new TreeMap<String, HighSymbol>();
|
||||
for (HighSymbol highSymbol : symbolMap.values()) {
|
||||
newMap.put(highSymbol.getName(), highSymbol);
|
||||
}
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given HighSymbol from this container.
|
||||
* The key is removed from the main symbolMap. It is also removed from the MappedEntry map
|
||||
* and from the list of parameter symbols if applicable.
|
||||
* @param highSymbol is the given symbol
|
||||
*/
|
||||
private void removeSymbol(HighSymbol highSymbol) {
|
||||
SymbolEntry mapEntry = highSymbol.getFirstWholeMap();
|
||||
if (mapEntry instanceof MappedEntry) {
|
||||
MappedVarKey key = new MappedVarKey(mapEntry.getStorage(), mapEntry.getPCAdress());
|
||||
addrMappedSymbols.remove(key);
|
||||
}
|
||||
symbolMap.remove(highSymbol.getId());
|
||||
if (highSymbol.isParameter()) {
|
||||
int index = highSymbol.getCategoryIndex();
|
||||
HighSymbol[] newArray = new HighSymbol[paramSymbols.length - 1];
|
||||
for (int i = 0; i < index; ++i) {
|
||||
newArray[i] = paramSymbols[i];
|
||||
}
|
||||
for (int i = index + 1; i < paramSymbols.length; ++i) {
|
||||
HighSymbol paramSym = paramSymbols[i];
|
||||
newArray[i - 1] = paramSym;
|
||||
paramSym.categoryIndex -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given names of the form: "baseName", "baseName$1", "baseName@2", ...
|
||||
* find the corresponding HighSymbols in this container and merge them into a single HighSymbol.
|
||||
* The name passed into this method must be of the form "baseName$1", the base name is extracted from it.
|
||||
* @param name is a string with the base name concatenated with "$1"
|
||||
* @param nameMap is a map from all symbols names in this container to the corresponding HighSymbol
|
||||
*/
|
||||
private void mergeNamedSymbols(String name, Map<String, HighSymbol> nameMap) {
|
||||
String baseName = name.substring(0, name.length() - 2);
|
||||
HighSymbol baseSymbol = nameMap.get(baseName);
|
||||
if (baseSymbol == null || !baseSymbol.isTypeLocked() ||
|
||||
(baseSymbol instanceof EquateSymbol)) {
|
||||
return;
|
||||
}
|
||||
DataType baseDataType = baseSymbol.getDataType();
|
||||
for (int index = 1;; ++index) {
|
||||
String nextName = baseName + '$' + Integer.toString(index);
|
||||
HighSymbol nextSymbol = nameMap.get(nextName);
|
||||
if (nextSymbol == null || !nextSymbol.isTypeLocked() || nextSymbol.isParameter() ||
|
||||
(baseSymbol instanceof EquateSymbol)) {
|
||||
break;
|
||||
}
|
||||
if (!nextSymbol.getDataType().equals(baseDataType)) { // Data-types of symbols being merged must match
|
||||
break;
|
||||
}
|
||||
SymbolEntry mapEntry = nextSymbol.getFirstWholeMap();
|
||||
if (mapEntry.getPCAdress() == null) { // Don't merge from an address tied symbol
|
||||
break;
|
||||
}
|
||||
baseSymbol.addMapEntry(mapEntry);
|
||||
removeSymbol(nextSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the local variable map from information attached to the Program DB's function.
|
||||
* @param includeDefaultNames is true if default symbol names should be considered locked
|
||||
*/
|
||||
public void grabFromFunction(boolean includeDefaultNames) {
|
||||
ArrayList<String> mergeNames = null;
|
||||
Function dbFunction = func.getFunction();
|
||||
Variable locals[] = dbFunction.getLocalVariables();
|
||||
for (Variable local : locals) {
|
||||
|
@ -94,6 +168,15 @@ public class LocalSymbolMap {
|
|||
istypelock = false;
|
||||
}
|
||||
String name = local.getName();
|
||||
if (name.length() > 2 && name.charAt(name.length() - 2) == '$') {
|
||||
// An indication of names like "name", "name@1", "name@2"
|
||||
if (name.charAt(name.length() - 1) == '1') {
|
||||
if (mergeNames == null) {
|
||||
mergeNames = new ArrayList<String>();
|
||||
}
|
||||
mergeNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
VariableStorage storage = local.getVariableStorage();
|
||||
long id = 0;
|
||||
|
@ -132,6 +215,15 @@ public class LocalSymbolMap {
|
|||
}
|
||||
DataType dt = var.getDataType();
|
||||
String name = var.getName();
|
||||
if (name.length() > 2 && name.charAt(name.length() - 2) == '$') {
|
||||
// An indication of names like "name", "name@1", "name@2"
|
||||
if (name.charAt(name.length() - 1) == '1') {
|
||||
if (mergeNames == null) {
|
||||
mergeNames = new ArrayList<String>();
|
||||
}
|
||||
mergeNames.add(name);
|
||||
}
|
||||
}
|
||||
VariableStorage storage = var.getVariableStorage();
|
||||
Address resAddr = storage.isStackStorage() ? null : pcaddr;
|
||||
long id = 0;
|
||||
|
@ -154,6 +246,7 @@ public class LocalSymbolMap {
|
|||
Arrays.sort(paramSymbols, PARAM_SYMBOL_SLOT_COMPARATOR);
|
||||
|
||||
grabEquates(dbFunction);
|
||||
grabMerges(mergeNames);
|
||||
}
|
||||
|
||||
private boolean isUserDefinedName(String name) {
|
||||
|
@ -450,10 +543,18 @@ public class LocalSymbolMap {
|
|||
}
|
||||
}
|
||||
|
||||
private void grabMerges(ArrayList<String> mergeNames) {
|
||||
if (mergeNames == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, HighSymbol> nameToSymbolMap = getNameToSymbolMap();
|
||||
for (String name : mergeNames) {
|
||||
mergeNamedSymbols(name, nameToSymbolMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashing keys for Local variables
|
||||
*
|
||||
*
|
||||
*/
|
||||
class MappedVarKey {
|
||||
private Address addr;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue