Merge remote-tracking branch 'origin/GP-2823_emteere_ArbitraryOverlappingFunctionBodies--SQUASHED'

This commit is contained in:
Ryan Kurtz 2023-08-24 11:14:53 -04:00
commit e2be5bb275
3 changed files with 110 additions and 135 deletions

View file

@ -265,8 +265,9 @@ public class SharedReturnAnalysisCmd extends BackgroundCommand {
processFunctionJumpReferences(program, entry, monitor); processFunctionJumpReferences(program, entry, monitor);
} }
else { else {
// check if there is any fallthru flow to the potential entry point // check if there could be any fallthru flow to the potential entry point
if (hasFallThruTo(program, entry)) { if (checkIfCouldHaveFallThruTo(program, entry)) {
// if there could be, even later in analysis, don't create the function
return; return;
} }
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program); AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
@ -274,7 +275,7 @@ public class SharedReturnAnalysisCmd extends BackgroundCommand {
} }
} }
private boolean hasFallThruTo(Program program, Address location) { private boolean checkIfCouldHaveFallThruTo(Program program, Address location) {
Instruction instr= program.getListing().getInstructionAt(location); Instruction instr= program.getListing().getInstructionAt(location);
if (instr == null) { if (instr == null) {
return true; return true;
@ -283,12 +284,16 @@ public class SharedReturnAnalysisCmd extends BackgroundCommand {
if (fallFrom != null) { if (fallFrom != null) {
Instruction fallInstr = program.getListing().getInstructionContaining(fallFrom); Instruction fallInstr = program.getListing().getInstructionContaining(fallFrom);
if (fallInstr != null && location.equals(fallInstr.getFallThrough())) { if (fallInstr != null && location.equals(fallInstr.getFallThrough())) {
// if there is a function above, then it falls into this routine // if there is no instruction yet, function may not be created yet
if (program.getFunctionManager().getFunctionContaining(fallFrom) != null) { return true;
return true;
}
} }
} }
if (instr.getFlowType() == RefType.TERMINATOR) {
// a single instruction that is terminal consider
// as having a possible future fallthru to
return true;
}
return false; return false;
} }

View file

@ -89,6 +89,7 @@ public class CreateFunctionCmd extends BackgroundCommand {
* Constructs a new command for creating functions that automatically computes * Constructs a new command for creating functions that automatically computes
* the body of each function. * the body of each function.
* @param entries the entry points at which to create functions. * @param entries the entry points at which to create functions.
* @param findEntryPoint true if entry point is unknown and should be found
*/ */
public CreateFunctionCmd(AddressSetView entries, boolean findEntryPoint) { public CreateFunctionCmd(AddressSetView entries, boolean findEntryPoint) {
this(null, entries, null, SourceType.DEFAULT, findEntryPoint, false); this(null, entries, null, SourceType.DEFAULT, findEntryPoint, false);
@ -107,6 +108,7 @@ public class CreateFunctionCmd extends BackgroundCommand {
* Constructs a new command for creating functions that automatically computes * Constructs a new command for creating functions that automatically computes
* the body of each function. * the body of each function.
* @param entries the entry points at which to create functions. * @param entries the entry points at which to create functions.
* @param source SourceType for created function
*/ */
public CreateFunctionCmd(AddressSetView entries, SourceType source) { public CreateFunctionCmd(AddressSetView entries, SourceType source) {
this(null, entries, null, source, false, false); this(null, entries, null, source, false, false);
@ -156,7 +158,6 @@ public class CreateFunctionCmd extends BackgroundCommand {
monitor.initialize(origEntries.getNumAddresses()); monitor.initialize(origEntries.getNumAddresses());
CodeBlockModel functionModel = null;
AddressIterator iter = origEntries.getAddresses(true); AddressIterator iter = origEntries.getAddresses(true);
while (iter.hasNext() && !monitor.isCancelled()) { while (iter.hasNext() && !monitor.isCancelled()) {
monitor.setProgress(++count); monitor.setProgress(++count);
@ -209,13 +210,19 @@ public class CreateFunctionCmd extends BackgroundCommand {
} }
monitor.setMessage("Function " + funcName); monitor.setMessage("Function " + funcName);
if (functionModel == null) { boolean didCreate = false;
functionModel = new PartitionCodeSubModel(program); try {
didCreate = createFunction(monitor, funcName, nameSpace, origEntry,
origBody, tmpSource);
} catch (OverlappingFunctionException e) {
// try to create again, sometimes thunks can get resolved that
// can't be detected, for example a thunk->function->thunk
// where the final thunk gets created
didCreate = createFunction(monitor, funcName, nameSpace, origEntry,
origBody, tmpSource);
} }
// TODO: What if function already exists ?? if (didCreate) {
if (createFunction(monitor, funcName, functionModel, nameSpace, origEntry,
origBody, tmpSource)) {
functionsCreated++; functionsCreated++;
} }
else { else {
@ -244,6 +251,8 @@ public class CreateFunctionCmd extends BackgroundCommand {
/** /**
* Returns function if create command was successful * Returns function if create command was successful
*
* @return last new created function, null if failed
*/ */
public Function getFunction() { public Function getFunction() {
return newFunc; return newFunc;
@ -259,13 +268,15 @@ public class CreateFunctionCmd extends BackgroundCommand {
* the body of the new function. * the body of the new function.
* @param nameSource * @param nameSource
* the source of this function's name * the source of this function's name
* @throws OverlappingFunctionException *
* @throws InvalidInputException * @return true if a function was created, or already exists
* @throws DuplicateNameException *
* @throws OverlappingFunctionException if new function overlaps with existing and couldn't fix
* @throws InvalidInputException bad characters in function name
*/ */
private boolean createFunction(TaskMonitor monitor, String funcName, private boolean createFunction(TaskMonitor monitor, String funcName,
CodeBlockModel functionModel, Namespace nameSpace, Address entry, AddressSetView body, Namespace nameSpace, Address entry, AddressSetView body,
SourceType nameSource) throws DuplicateNameException, InvalidInputException, SourceType nameSource) throws InvalidInputException,
OverlappingFunctionException, CancelledException { OverlappingFunctionException, CancelledException {
FunctionManager functionMgr = program.getFunctionManager(); FunctionManager functionMgr = program.getFunctionManager();
@ -282,7 +293,7 @@ public class CreateFunctionCmd extends BackgroundCommand {
entry = functionContaining.getEntryPoint(); // preserve entry entry = functionContaining.getEntryPoint(); // preserve entry
long bodySize = functionContaining.getBody().getNumAddresses(); long bodySize = functionContaining.getBody().getNumAddresses();
if (bodySize != 1) { if (bodySize != 1) {
return false; return true; // function already created
} }
} }
@ -323,21 +334,8 @@ public class CreateFunctionCmd extends BackgroundCommand {
// get the function body JUST by following flow // get the function body JUST by following flow
body = (body == null ? getFunctionBody(program, entry, false, monitor) : body); body = (body == null ? getFunctionBody(program, entry, false, monitor) : body);
// subtract out any existing functions that overlap the body
AddressSetView oldbody = body;
body = removeExistingFunctionsFromBody(entry, body, monitor);
if (body == null) {
return false;
}
Map<Function, AddressSetView> bodyChangeMap = new HashMap<>(); Map<Function, AddressSetView> bodyChangeMap = new HashMap<>();
// If I ain't got nobody left after extracting overlapping functions body = subtractBodyFromExisting(program, entry, body, bodyChangeMap, monitor);
if (body.isEmpty() || !body.contains(entry)) {
// try subtracting this body from existing functions
// need to compute a bodyChangeMap if affecting other functions
// in case creation of this function fails
body = subtractBodyFromExisting(entry, oldbody, bodyChangeMap, monitor);
}
return createFunction(nameSpace, funcName, entry, body, nameSource, bodyChangeMap, monitor); return createFunction(nameSpace, funcName, entry, body, nameSource, bodyChangeMap, monitor);
} }
@ -352,15 +350,14 @@ public class CreateFunctionCmd extends BackgroundCommand {
* @param nameSource - source of the name * @param nameSource - source of the name
* @param bodyChangeMap - change map to restore other affected functions bodies if this fails * @param bodyChangeMap - change map to restore other affected functions bodies if this fails
* @param monitor task monitor * @param monitor task monitor
* @return * @return true if function created
* @throws OverlappingFunctionException * @throws OverlappingFunctionException if functions overlaps with existing function
* @throws DuplicateNameException * @throws InvalidInputException if function name has bad chars
* @throws InvalidInputException
*/ */
private boolean createFunction(Namespace nameSpace, String funcName, Address entry, private boolean createFunction(Namespace nameSpace, String funcName, Address entry,
AddressSetView body, SourceType nameSource, Map<Function, AddressSetView> bodyChangeMap, AddressSetView body, SourceType nameSource, Map<Function, AddressSetView> bodyChangeMap,
TaskMonitor monitor) TaskMonitor monitor)
throws OverlappingFunctionException, DuplicateNameException, InvalidInputException { throws OverlappingFunctionException, InvalidInputException {
Listing listing = program.getListing(); Listing listing = program.getListing();
@ -386,8 +383,7 @@ public class CreateFunctionCmd extends BackgroundCommand {
} }
newFunc = listing.createFunction(funcName, nameSpace, entry, body, nameSource); newFunc = listing.createFunction(funcName, nameSpace, entry, body, nameSource);
} }
catch (InvalidInputException e) { catch (InvalidInputException | OverlappingFunctionException e) {
restoreOriginalBodies(bodyChangeMap);
throw e; throw e;
} }
@ -395,77 +391,73 @@ public class CreateFunctionCmd extends BackgroundCommand {
} }
/** /**
* subtract this functions entire body from existing functions * Subtract this functions entire body from existing functions.
* NewFuncBody keeps any overlapping code that occurs between its entry point and the next entry point.
* This rule helps functions keep a contiguous function body if functions share code such as a return, or
* even an internal branch.
*
* NOTE: This will change the other functions bodies
* *
* @param entry - entry point of new function * @param entry - entry point of new function
* @param body - new functions body * @param newFuncBody - new functions body
* @param bodyChangeMap - map of functions that have their bodies changed by creating this function * @param bodyChangeMap - map of functions that have their bodies changed by creating this function
* @param monitor task monitor * @param monitor task monitor
* @return *
* @throws CancelledException * @return address set of non-overlapping function body, some addresses may be grabbed from existing functions
* @throws OverlappingFunctionException *
* @throws CancelledException user cancelled
* @throws OverlappingFunctionException existing functions overlap and couldn't reconcile
*/ */
private AddressSetView subtractBodyFromExisting(Address entry, AddressSetView body, private static AddressSetView subtractBodyFromExisting(Program program, Address entry, AddressSetView newFuncBody,
Map<Function, AddressSetView> bodyChangeMap, TaskMonitor monitor) Map<Function, AddressSetView> bodyChangeMap, TaskMonitor monitor)
throws CancelledException, OverlappingFunctionException { throws CancelledException, OverlappingFunctionException {
Iterator<Function> iter = program.getFunctionManager().getFunctionsOverlapping(body); // get all functions that overlap new function body
while (iter.hasNext()) { Iterator<Function> iter = program.getFunctionManager().getFunctionsOverlapping(newFuncBody);
monitor.checkCancelled();
Function elem = iter.next();
AddressSetView funcBody = elem.getBody();
if (funcBody.contains(entry)) {
bodyChangeMap.put(elem, funcBody);
// re-define the function that does contain me....
funcBody = funcBody.subtract(body);
elem.setBody(funcBody);
}
else {
// else, the body flowed into an existing function
body = body.subtract(funcBody);
}
}
return body;
}
/**
* Remove any existing functions bodies from the new functions body at entry.
*
* @param entry
* @param body
* @param monitor task monitor
* @return the new body, or null if body could not be created and need to abort function creation.
*
* @throws CancelledException
*/
private AddressSetView removeExistingFunctionsFromBody(Address entry, AddressSetView body,
TaskMonitor monitor) throws CancelledException {
Iterator<Function> iter = program.getFunctionManager().getFunctionsOverlapping(body);
while (iter.hasNext()) { while (iter.hasNext()) {
monitor.checkCancelled(); monitor.checkCancelled();
Function elem = iter.next(); Function overlapFunc = iter.next();
if (elem.getEntryPoint().equals(entry)) { Address overlapEntryPoint = overlapFunc.getEntryPoint();
// if finding the entrypoint, need to redefine the functions body. if (overlapEntryPoint.equals(entry)) {
if (!findEntryPoint) { // function found is possibly existing function
long bodySize = elem.getBody().getNumAddresses(); continue;
// if not finding the entry point, and bodysize > 1, bad function }
if (bodySize != 1) { AddressSetView overlapFuncBody = overlapFunc.getBody();
return null; // catch functions that are place-holders for a function yet to be disassembled
} if (overlapFuncBody.getNumAddresses() == 1) {
overlapFuncBody = getFunctionBody(program, overlapEntryPoint, false, monitor);
if (overlapFuncBody.contains(entry)) {
continue;
} }
} }
else {
AddressSetView bodyInConflict = elem.getBody(); // get new body for overlapping function, by carving out addresses that
// catch functions that are place-holders for a function yet to be disassembled // should belong in the new function
if (bodyInConflict.getNumAddresses() == 1) { Address overlapEndAddr;
bodyInConflict = getFunctionBody(program, elem.getEntryPoint(), false, monitor); if (overlapEntryPoint.compareTo(entry) < 0) {
if (bodyInConflict.contains(entry)) { // overlap function is before, subtract from entry to end of new function
continue; overlapEndAddr = newFuncBody.getMaxAddress();
} } else {
} // overlap function is after, subtract from entry to entry of overlapping function
body = body.subtract(bodyInConflict); overlapEndAddr = overlapEntryPoint.previous();
} }
AddressSet overlapAddrsShouldBeInNewFunction = new AddressSet(entry,overlapEndAddr);
AddressSetView newOverlapFuncBody = overlapFuncBody.subtract(overlapAddrsShouldBeInNewFunction);
try {
if (!overlapFuncBody.equals(newOverlapFuncBody)) {
overlapFunc.setBody(newOverlapFuncBody); // set overlap body with new function body addresses removed
bodyChangeMap.put(overlapFunc, overlapFuncBody); // save old overlap body
}
overlapFuncBody = newOverlapFuncBody;
} catch (OverlappingFunctionException oe) {
// do nothing, will just subtract entire overlapping body from new function body
}
// remove overlapping functions body from new function body
newFuncBody = newFuncBody.subtract(overlapFuncBody);
} }
return body;
return newFuncBody;
} }
private boolean handleExistingFunction(TaskMonitor monitor, Address entry, private boolean handleExistingFunction(TaskMonitor monitor, Address entry,
@ -475,7 +467,7 @@ public class CreateFunctionCmd extends BackgroundCommand {
if (bodySize > 1) { if (bodySize > 1) {
if (!recreateFunction) { if (!recreateFunction) {
// Function at entry already exists and recreateFunction not enabled // Function at entry already exists and recreateFunction not enabled
return false; return true;
} }
// if it is a thunk, then we're done // if it is a thunk, then we're done
if (resolveThunk(entry, null, monitor)) { if (resolveThunk(entry, null, monitor)) {
@ -498,7 +490,7 @@ public class CreateFunctionCmd extends BackgroundCommand {
* @param monitor task monitor * @param monitor task monitor
* @return true if the entry resolved to a thunk * @return true if the entry resolved to a thunk
* *
* @throws OverlappingFunctionException * @throws OverlappingFunctionException if thunk thunks itself
*/ */
private boolean resolveThunk(Address entry, AddressSetView body, TaskMonitor monitor) private boolean resolveThunk(Address entry, AddressSetView body, TaskMonitor monitor)
throws OverlappingFunctionException { throws OverlappingFunctionException {
@ -528,9 +520,9 @@ public class CreateFunctionCmd extends BackgroundCommand {
/** /**
* using the body map revert any changes made to function bodies * using the body map revert any changes made to function bodies
* *
* @param bodyChangeMap * @param bodyChangeMap map of functions to original bodies
*/ */
private void restoreOriginalBodies(Map<Function, AddressSetView> bodyChangeMap) { private static void restoreOriginalBodies(Map<Function, AddressSetView> bodyChangeMap) {
Set<Map.Entry<Function, AddressSetView>> entries = bodyChangeMap.entrySet(); Set<Map.Entry<Function, AddressSetView>> entries = bodyChangeMap.entrySet();
Iterator<Map.Entry<Function, AddressSetView>> iter = entries.iterator(); Iterator<Map.Entry<Function, AddressSetView>> iter = entries.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
@ -599,17 +591,9 @@ public class CreateFunctionCmd extends BackgroundCommand {
* @return AddressSetView address set representing the body of the function * @return AddressSetView address set representing the body of the function
*/ */
public static AddressSetView getFunctionBody(TaskMonitor monitor, Program program, public static AddressSetView getFunctionBody(TaskMonitor monitor, Program program,
Address entry) throws CancelledException { Address entry) {
CodeBlock block = null;
PartitionCodeSubModel model = new PartitionCodeSubModel(program); return getFunctionBody(program, entry, true, monitor);
// MultEntSubModel model = new MultEntSubModel(program);
block = model.getCodeBlockAt(entry, monitor);
if (block == null) {
return getFunctionBody(program, entry);
}
return block;
} }
/** /**
@ -705,30 +689,17 @@ public class CreateFunctionCmd extends BackgroundCommand {
func.setBody(newBody); // trigger analysis func.setBody(newBody); // trigger analysis
} }
catch (OverlappingFunctionException e) { catch (OverlappingFunctionException e) {
// subtract out any existing functions that overlap the body
Iterator<Function> iter = program.getFunctionManager().getFunctionsOverlapping(newBody); Map<Function, AddressSetView> bodyChangeMap = new HashMap<>();
while (iter.hasNext()) {
monitor.checkCancelled();
Function elem = iter.next();
if (elem.getEntryPoint().equals(entry)) {
// if finding the entrypoint, need to redefine the functions body.
continue;
}
AddressSetView bodyInConflict = elem.getBody();
// catch functions that are place-holders for a function yet to be disassembled
if (bodyInConflict.getNumAddresses() == 1) {
bodyInConflict = getFunctionBody(program, elem.getEntryPoint(), false, monitor);
if (bodyInConflict.contains(entry)) {
continue;
}
}
newBody = newBody.subtract(bodyInConflict);
}
try { try {
newBody = subtractBodyFromExisting(program, entry, newBody, bodyChangeMap, monitor);
func.setBody(newBody); // trigger analysis func.setBody(newBody); // trigger analysis
} }
catch (OverlappingFunctionException exc) { catch (CancelledException | OverlappingFunctionException e1) {
// something went wrong, put things back the way they were
restoreOriginalBodies(bodyChangeMap);
return false; return false;
} }
} }

View file

@ -671,10 +671,9 @@ public class AddressTable {
func.setBody(funcBody); func.setBody(funcBody);
} }
catch (OverlappingFunctionException e2) { catch (OverlappingFunctionException e2) {
// do nothing
} }
} }
catch (CancelledException e3) {
}
} }
} }