diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java index ebc15b5ed8..5e32b90823 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java @@ -116,7 +116,8 @@ public class DecompilerScriptUtils { if (decompRes == null || decompRes.getHighFunction() == null || decompRes.getHighFunction().getFunctionPrototype() == null) { - Msg.debug(this, "Couldn't commit params - null high function"); + Msg.debug(this, "Couldn't commit params - null high function " + + function.getEntryPoint().toString()); return; } @@ -125,11 +126,13 @@ public class DecompilerScriptUtils { ReturnCommitOption.COMMIT, SourceType.ANALYSIS); } catch (DuplicateNameException e) { - Msg.debug(this, "Couldn't commit params " + e); + Msg.debug(this, + "Couldn't commit params for " + function.getEntryPoint().toString() + " " + e); return; } catch (InvalidInputException e) { - Msg.debug(this, "Couldn't commit params " + e); + Msg.debug(this, + "Couldn't commit params for " + function.getEntryPoint().toString() + " " + e); return; } } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java index 1a05567e36..de1fab842e 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java @@ -324,6 +324,12 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { functionAddress = functionAddress.getNewAddress(longValue); } + // skip anything that is data - this could get passed in if it is an external ptr to a function + Data data = getDataAt(functionAddress); + if (data != null) { + return null; + } + Function function = getFunctionAt(functionAddress); if (function == null) { // try to create function @@ -1002,6 +1008,9 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { * @return the referenced function or null if no function is referenced * @throws CancelledException if cancelled */ + //TODO: this is same as getReferencedFunction at line 313 except for the thunked function + // argument but is missing the lowBit code stuff - combine both and replace uses to use the + // one merged version - this one has loop and the other just works if one ref public Function getReferencedFunction(Address address, boolean getThunkedFunction) throws CancelledException { @@ -1020,6 +1029,19 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { continue; } + Register lowBitCodeMode = currentProgram.getRegister("LowBitCodeMode"); + if (lowBitCodeMode != null) { + long longValue = referencedAddress.getOffset(); + longValue = longValue & ~0x1; + referencedAddress = referencedAddress.getNewAddress(longValue); + } + + // skip anything that is data - this could get passed in if it is an external ptr to a function + Data data = getDataAt(referencedAddress); + if (data != null) { + continue; + } + Function function = getFunctionAt(referencedAddress); if (function == null) { @@ -1197,7 +1219,7 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { } /** - * Method to generate unique shorted names for classes with templates + * Method to generate unique shortened names for classes with templates * @param recoveredClasses the list of classes in the program * @throws CancelledException if cancelled */ diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index 33c3633578..4495c7538e 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -167,15 +167,16 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // using all the information found above, create the class structures, add the constructor, // destructor, vfunctions to class which finds the appropriate class structure and assigns // to "this" param - monitor.setMessage("Creating class data types and applying class structures"); + monitor.setMessage("Figuring out class data members..."); figureOutClassDataMembers(recoveredClasses); if (USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS) { extendedFlatAPI.createShortenedTemplateNamesForClasses(recoveredClasses); } - + monitor.setMessage("Creating class data types and applying class structures..."); createAndApplyClassStructures(recoveredClasses); + monitor.setMessage("Finishing up..."); // fix purecall vfunction definitions fixupPurecallFunctionDefs(); @@ -1299,12 +1300,15 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // lists to remove functions that are also on vfunction lists trimConstructorDestructorLists(recoveredClasses, allVftables); + monitor.setMessage("... determining operator_delete and new functions"); determineOperatorDeleteAndNewFunctions(allVftables); // find deleting destructors + monitor.setMessage("... finding deleting destructors"); findDeletingDestructors(recoveredClasses, allVftables); // use atexit param list to find more destructors + monitor.setMessage("... finding destructors using atexit calls"); findDestructorsUsingAtexitCalledFunctions(recoveredClasses); // figure out which are inlined and put on separate list to be processed later @@ -1312,33 +1316,43 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // figure out which member functions are constructors and which are destructors // using the order their parents are called + monitor.setMessage("... processing constructors and destructors using call order"); processRegularConstructorsAndDestructorsUsingCallOrder(recoveredClasses); // determine which of the inlines are constructors and which are destructors + monitor.setMessage("... processing inlineds"); processInlinedConstructorsAndDestructors(recoveredClasses); + monitor.setMessage("... finding more constructors and destructors"); findConstructorsAndDestructorsUsingAncestorClassFunctions(recoveredClasses); + monitor.setMessage("... finding more inlines"); findInlineConstructorsAndDestructorsUsingRelatedClassFunctions(recoveredClasses); // use the load/store information from decompiler to figure out as many of the // ones that could not be determined in earlier stages + monitor.setMessage("... processing remaining indeterminate constructors and destructors"); processRemainingIndeterminateConstructorsAndDestructors(recoveredClasses); // use the known constructors and known vfunctions to figure out basic clone functions + monitor.setMessage("... finding basic clones"); findBasicCloneFunctions(recoveredClasses); // This has to be here. It needs all the info from the previously run methods to do this. // Finds the constructors that have multiple basic blocks, reference the vftable not in the // first block, and call non-parent constructors and non operator new before the vftable ref + monitor.setMessage("... finding more inlined constructors"); findMoreInlinedConstructors(recoveredClasses); + monitor.setMessage("... finding destructors with no params"); findDestructorsWithNoParamsOrReturn(recoveredClasses); // use vftables with references to all the same function (except possibly one deleting // destructor)to find the purecall function + monitor.setMessage("... identifying pure virtual function"); identifyPureVirtualFunction(recoveredClasses); + monitor.setMessage("... finding real vbase functions"); findRealVBaseFunctions(recoveredClasses); // make constructors and destructors _thiscalls @@ -1905,8 +1919,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { parentOrderMap = new HashMap(); - Map referenceToParentMap = - getReferenceToClassMap(recoveredClass, function); + Map referenceToParentMap = + getReferenceToReferencedObjectsMap(recoveredClass, function); Map allowedReferncesToParentMap = new HashMap(); @@ -1933,8 +1947,9 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { recoveredClass.getVftableAddresses().contains(possibleVftable)) { continue; } - - RecoveredClass ancestorClass = referenceToParentMap.get(classReferenceAddress); + ReferencedClassObject referencedClassObject = + referenceToParentMap.get(classReferenceAddress); + RecoveredClass ancestorClass = referencedClassObject.getContainingClass(); if (allowedAncestors.contains(ancestorClass)) { allowedReferncesToParentMap.put(classReferenceAddress, ancestorClass); } @@ -1955,7 +1970,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // iterate over the ordered parents and add the order to the parent map for (Address refAddress : parentReferences) { monitor.checkCancelled(); - RecoveredClass parentClass = referenceToParentMap.get(refAddress); + ReferencedClassObject referencedClassObject = referenceToParentMap.get(refAddress); + RecoveredClass parentClass = referencedClassObject.getContainingClass(); parentOrderMap.put(order, parentClass); order++; } @@ -2077,16 +2093,12 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { private void createAndApplyClassStructures(List recoveredClasses) throws CancelledException, Exception { - List listOfClasses = new ArrayList(recoveredClasses); - - Iterator recoveredClassIterator = recoveredClasses.iterator(); + List processedClasses = new ArrayList<>(); // first process all the classes with no parents - while (recoveredClassIterator.hasNext()) { + for (RecoveredClass recoveredClass : recoveredClasses) { monitor.checkCancelled(); - RecoveredClass recoveredClass = recoveredClassIterator.next(); - if (recoveredClass.hasMultipleInheritance()) { continue; } @@ -2097,20 +2109,19 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { if (!recoveredClass.hasVftable()) { createClassStructureWhenNoParentOrVftable(recoveredClass); - listOfClasses.remove(recoveredClass); + processedClasses.add(recoveredClass); continue; } processDataTypes(recoveredClass); - listOfClasses.remove(recoveredClass); - + processedClasses.add(recoveredClass); } // now process the classes that have all parents processed // continue looping until all classes are processed int numLoops = 0; - while (!listOfClasses.isEmpty()) { + while (processedClasses.size() < recoveredClasses.size()) { monitor.checkCancelled(); // put in stop gap measure in case some classes never get all @@ -2120,13 +2131,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { } numLoops++; - recoveredClassIterator = recoveredClasses.iterator(); - while (recoveredClassIterator.hasNext()) { - - RecoveredClass recoveredClass = recoveredClassIterator.next(); - + for (RecoveredClass recoveredClass : recoveredClasses) { monitor.checkCancelled(); - if (!listOfClasses.contains(recoveredClass)) { + + if (processedClasses.contains(recoveredClass)) { continue; } @@ -2135,8 +2143,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { } processDataTypes(recoveredClass); - listOfClasses.remove(recoveredClass); - + processedClasses.add(recoveredClass); } } } @@ -2673,11 +2680,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { continue; } if (numAddressRanges == 2) { - // else fixup split dd function + // else possible split dd function - try to split and created second function + // if it is one Function scalarDeletingDestructor = createSplitDeletingDestructorFunction(body); if (scalarDeletingDestructor == null) { - Msg.debug(this, "Could not fixup split deleting destructor function: " + - function.getEntryPoint()); continue; } fixupSplitDeletingDestructorSymbols(function, scalarDeletingDestructor); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java index 2e02d61544..fc0c19dca6 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -96,7 +96,6 @@ public class RecoveredClass { TaskMonitor monitor = TaskMonitor.DUMMY; - RecoveredClass(String name, CategoryPath classPath, Namespace classNamespace, DataTypeManager dataTypeManager) { this.name = name; @@ -519,19 +518,19 @@ public class RecoveredClass { // if the new component is a non-empty structure, check to see if the current // structure has undefined or equivalent components and replace with new struct if so if (newComponentDataType instanceof Structure) { - + // if new component is any empty placeholder structure AND if the existing component // is undefined then replace with undefined1 dt if (newComponentDataType.isNotYetDefined()) { if (Undefined.isUndefined(currentComponentDataType)) { computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1, - fieldName, comment); + fieldName, comment); } continue; } if (EditStructureUtils.hasReplaceableComponentsAtOffset(computedClassStructure, - offset, (Structure)newComponentDataType, monitor)) { - + offset, (Structure) newComponentDataType, monitor)) { + boolean successfulClear = EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset, length, monitor); @@ -675,4 +674,3 @@ public class RecoveredClass { return shortenedTemplateName; } } - diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index 1d32ee1009..6053f00f84 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -141,6 +141,15 @@ public class RecoveredClassHelper { public HashMap> allVfunctions = new HashMap<>(); + public HashMap> functionCallMapFollowThunks = + new HashMap<>(); + public HashMap> functionCallMapDontFollowThunks = + new HashMap<>(); + + public HashMap referencedObjectMap = new HashMap<>(); + public HashMap> functionReferenceMap = + new HashMap<>(); + public RecoveredClassHelper(Program program, ServiceProvider serviceProvider, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, boolean nameVunctions, boolean makeVfunctionsThisCalls, TaskMonitor monitor) @@ -416,16 +425,30 @@ public class RecoveredClassHelper { * @param function the given function * @param getThunkedFunction if true, use the thunked function in the map, if false use the * directly called function from the calling function even if it is a thunk - * @param visited the set of function entry point addresses already processed * @return a map of the given functions calling addresses to the called functions * @throws CancelledException if cancelled */ - public Map getFunctionCallMap(Function function, boolean getThunkedFunction, - Set
visited) + public Map getFunctionCallMap(Function function, boolean getThunkedFunction) throws CancelledException { - visited.add(function.getEntryPoint()); - Map functionCallMap = new HashMap(); + Map functionCallMap; + + if (getThunkedFunction) { + functionCallMap = functionCallMapFollowThunks.get(function); + } + else { + functionCallMap = functionCallMapDontFollowThunks.get(function); + } + + if (functionCallMap != null) { + return functionCallMap; + } + + // initializing here not above because checking for empty map with the null + // wouldn't be correct because the below could generate an empty map if there are no + // calls at all. The above map check/return needs to distinguish between the not in map + // case and the already processed empty map case + functionCallMap = new HashMap(); InstructionIterator instructions = function.getProgram().getListing().getInstructions(function.getBody(), true); @@ -442,8 +465,14 @@ public class RecoveredClassHelper { continue; } + if (calledFunction.isExternal()) { + continue; + } + // include the null functions in map so things using map can get accurate count // of number of CALL instructions even if the call reg type + //TODO: the above continue is preventing the nulls and exts here - do we want this? + // or do we want another map/option? functionCallMap.put(instruction.getMinAddress(), calledFunction); } if (instruction.getFlowOverride().equals(FlowOverride.CALL_RETURN)) { @@ -454,10 +483,9 @@ public class RecoveredClassHelper { Address functionAddress = reference.getFromAddress(); Function secondHalfOfFunction = extendedFlatAPI.getReferencedFunction(functionAddress); - if (secondHalfOfFunction != null && - !visited.contains(secondHalfOfFunction.getEntryPoint())) { + if (secondHalfOfFunction != null) { Map functionCallMap2 = - getFunctionCallMap(secondHalfOfFunction, false, visited); + getFunctionCallMap(secondHalfOfFunction, false); for (Address addr : functionCallMap2.keySet()) { monitor.checkCancelled(); functionCallMap.put(addr, functionCallMap2.get(addr)); @@ -466,12 +494,14 @@ public class RecoveredClassHelper { } } - return functionCallMap; - } - public Map getFunctionCallMap(Function function, boolean getThunkedFunction) - throws CancelledException { - return getFunctionCallMap(function, getThunkedFunction, new HashSet<>()); + if (getThunkedFunction) { + functionCallMapFollowThunks.put(function, functionCallMap); + } + else { + functionCallMapDontFollowThunks.put(function, functionCallMap); + } + return functionCallMap; } public void updateNamespaceToClassMap(Namespace namespace, RecoveredClass recoveredClass) { @@ -714,19 +744,26 @@ public class RecoveredClassHelper { public List
getSortedListOfAncestorRefsInFunction(Function function, RecoveredClass recoveredClass) throws CancelledException { - // get the map of all referenced vftables or constructor/desstructor calls in this function - Map referenceToClassMapForFunction = - getReferenceToClassMap(recoveredClass, function); + // get the addresses in the function that refer to classes either by + // referencing a vftable in a class or by calling a function in a class + Map referenceToClassMap = + functionReferenceMap.get(function); + if (referenceToClassMap == null) { + referenceToClassMap = + getReferenceToReferencedObjectsMap(recoveredClass, function); + } // get a list of all ancestor classes referenced in the map List classHierarchy = recoveredClass.getClassHierarchy(); // make a list of all related class references List
listOfAncestorRefs = new ArrayList
(); - Set
ancestorRefs = referenceToClassMapForFunction.keySet(); + Set
ancestorRefs = referenceToClassMap.keySet(); for (Address ancestorRef : ancestorRefs) { monitor.checkCancelled(); - RecoveredClass mappedClass = referenceToClassMapForFunction.get(ancestorRef); + + ReferencedClassObject referencedClassObject = referenceToClassMap.get(ancestorRef); + RecoveredClass mappedClass = referencedClassObject.getContainingClass(); if (classHierarchy.contains(mappedClass)) { listOfAncestorRefs.add(ancestorRef); } @@ -745,10 +782,11 @@ public class RecoveredClassHelper { * @return Map of Address references to Class object for the given function * @throws CancelledException when cancelled */ - public Map getReferenceToClassMap(RecoveredClass recoveredClass, + public Map getReferenceToReferencedObjectsMap( + RecoveredClass recoveredClass, Function function) throws CancelledException { - Map referenceToParentMap = new HashMap(); + Map referenceToParentMap = new HashMap<>(); List
vftableRefs = functionToVftableRefsMap.get(function); @@ -768,7 +806,9 @@ public class RecoveredClassHelper { } RecoveredClass parentClass = vftableToClassMap.get(vftableAddress); - referenceToParentMap.put(vftableRef, parentClass); + ReferencedClassObject referencedObject = + new ReferencedClassObject(vftableRef, vftableAddress, parentClass); + referenceToParentMap.put(vftableRef, referencedObject); } // remove duplicate vftable refs (occasionally there are LEA then MOV of same vftable address @@ -808,7 +848,9 @@ public class RecoveredClassHelper { continue; } - referenceToParentMap.put(address, ancestorClass); + ReferencedClassObject referencedObject = + new ReferencedClassObject(address, calledFunction, ancestorClass); + referenceToParentMap.put(address, referencedObject); } return referenceToParentMap; @@ -840,7 +882,7 @@ public class RecoveredClassHelper { return badFIDFunctions; } - private Map dedupeMap(Map map) + private Map dedupeMap(Map map) throws CancelledException { // Sort the vftable refs in the order they appear in the function @@ -852,7 +894,9 @@ public class RecoveredClassHelper { Address lastVftableRef = null; for (Address vftableRef : vftableRefList) { monitor.checkCancelled(); - RecoveredClass currentClass = map.get(vftableRef); + + ReferencedClassObject referencedObject = map.get(vftableRef); + RecoveredClass currentClass = referencedObject.getContainingClass(); if (lastClass != null && lastClass.equals(currentClass)) { // if vftable refs are within a few instructions, dedupe map @@ -2108,17 +2152,21 @@ public class RecoveredClassHelper { * @throws DuplicateNameException if try to create same symbol name already in namespace * @throws CircularDependencyException if parent namespace is descendant of given namespace */ - public void createListedConstructorFunctions(Map referenceToClassMap, + public void createListedConstructorFunctions( + Map referenceToClassMap, List
referencesToConstructors) throws CancelledException, InvalidInputException, DuplicateNameException, CircularDependencyException { for (Address constructorReference : referencesToConstructors) { monitor.checkCancelled(); - RecoveredClass recoveredClass = referenceToClassMap.get(constructorReference); + ReferencedClassObject referencedClassObject = + referenceToClassMap.get(constructorReference); + + RecoveredClass recoveredClass = referencedClassObject.getContainingClass(); Function constructor = - extendedFlatAPI.getReferencedFunction(constructorReference, true); + referencedClassObject.getReferencedFunction(); if (constructor == null) { continue; @@ -2146,9 +2194,9 @@ public class RecoveredClassHelper { * @throws DuplicateNameException if try to create same symbol name already in namespace * @throws CircularDependencyException if parent namespace is descendant of given namespace */ - public void processInlineConstructor(RecoveredClass recoveredClass, - Function inlinedConstructorFunction, Map referenceToClassMap) + Function inlinedConstructorFunction, + Map referenceToClassMap) throws CancelledException, InvalidInputException, DuplicateNameException, CircularDependencyException { @@ -2161,6 +2209,7 @@ public class RecoveredClassHelper { List
referenceAddresses = new ArrayList
(referenceToClassMap.keySet()); for (Address reference : referenceAddresses) { monitor.checkCancelled(); + Address vftableAddress = getVftableAddress(reference); if (vftableAddress != null) { referencesToVftables.add(reference); @@ -2178,7 +2227,10 @@ public class RecoveredClassHelper { for (Address refToVftable : referencesToVftables) { monitor.checkCancelled(); - RecoveredClass referencedClass = referenceToClassMap.get(refToVftable); + + ReferencedClassObject referencedClassObject = referenceToClassMap.get(refToVftable); + + RecoveredClass referencedClass = referencedClassObject.getContainingClass(); // last reference is the constructor if (refToVftable.equals(lastRef)) { @@ -2206,7 +2258,8 @@ public class RecoveredClassHelper { * @throws DuplicateNameException if try to create same symbol name already in namespace */ public void processInlineDestructor(RecoveredClass recoveredClass, - Function inlinedDestructorFunction, Map referenceToClassMap) + Function inlinedDestructorFunction, + Map referenceToClassMap) throws CancelledException, InvalidInputException, DuplicateNameException { if (referenceToClassMap.isEmpty()) { @@ -2236,7 +2289,9 @@ public class RecoveredClassHelper { for (Address refToVftable : referencesToVftables) { monitor.checkCancelled(); - RecoveredClass referencedClass = referenceToClassMap.get(refToVftable); + ReferencedClassObject referencedClassObject = referenceToClassMap.get(refToVftable); + + RecoveredClass referencedClass = referencedClassObject.getContainingClass(); // last reference is the constructor if (refToVftable.equals(lastRef)) { @@ -2293,7 +2348,7 @@ public class RecoveredClassHelper { /** * Method to determine if the given function calls a known constructor or inlined constructor - * @param Set of called functions + * @param calledFunctions set of called functions * @return true if calling function calls a known constructor or inlined constructor, false otherwise * @throws CancelledException if cancelled */ @@ -2313,7 +2368,7 @@ public class RecoveredClassHelper { /** * Method to determine if the given function calls a known denstructor or inlined destructor - * @param Set of called functions + * @param calledFunctions Set of called functions * @return true if function calls a known constructor or inlined constructor, false otherwise * of its own or none * @throws CancelledException if cancelled @@ -2536,16 +2591,19 @@ public class RecoveredClassHelper { * @throws InvalidInputException if error setting return type * @throws DuplicateNameException if try to create same symbol name already in namespace */ - public void createListedDestructorFunctions(Map referenceToClassMap, + public void createListedDestructorFunctions( + Map referenceToClassMap, List
referencesToDestructors) throws CancelledException, InvalidInputException, DuplicateNameException { for (Address destructorReference : referencesToDestructors) { monitor.checkCancelled(); - RecoveredClass recoveredClass = referenceToClassMap.get(destructorReference); + ReferencedClassObject referencedClassObject = + referenceToClassMap.get(destructorReference); + RecoveredClass recoveredClass = referencedClassObject.getContainingClass(); - Function destructor = extendedFlatAPI.getReferencedFunction(destructorReference, true); + Function destructor = referencedClassObject.getReferencedFunction(); if (destructor == null) { continue; } @@ -4871,6 +4929,13 @@ public class RecoveredClassHelper { Symbol symbol = symbols.next(); + // function might be in multiple classes if compiler was optimized to use same function + // for multiple classes with identical functions + // let the class that has the primary symbol do the update + if (!symbol.isPrimary()) { + continue; + } + Function function = functionManager.getFunctionAt(symbol.getAddress()); if (function == null) { @@ -6145,6 +6210,8 @@ public class RecoveredClassHelper { throws CancelledException, InvalidInputException, DuplicateNameException, CircularDependencyException { + Set processedFunctions = new HashSet<>(); + for (RecoveredClass recoveredClass : recoveredClasses) { monitor.checkCancelled(); List inlineFunctionsList = @@ -6153,12 +6220,20 @@ public class RecoveredClassHelper { for (Function inlineFunction : inlineFunctionsList) { monitor.checkCancelled(); + // functions with inlines often contain multiple inlined constructor/destructors + // skip if already processed (processInline methods update all of them) + if (processedFunctions.contains(inlineFunction)) { + continue; + } + // get the addresses in the function that refer to classes either by // referencing a vftable in a class or by calling a function in a class - Map referenceToClassMap = - getReferenceToClassMap(recoveredClass, inlineFunction); - List
referencesToFunctions = - extendedFlatAPI.getReferencesToFunctions(referenceToClassMap); + Map referenceToClassMap = + functionReferenceMap.get(inlineFunction); + if (referenceToClassMap == null) { + referenceToClassMap = + getReferenceToReferencedObjectsMap(recoveredClass, inlineFunction); + } // if some of the references are to functions figure out if they are // constructors destructors or add them to list of indetermined @@ -6166,46 +6241,48 @@ public class RecoveredClassHelper { boolean isDestructor = false; List
referenceToIndeterminates = new ArrayList
(); - if (!referencesToFunctions.isEmpty()) { - for (Address functionReference : referencesToFunctions) { + for (Address referenceAddress : referenceToClassMap.keySet()) { - monitor.checkCancelled(); - Function function = - extendedFlatAPI.getReferencedFunction(functionReference, true); + monitor.checkCancelled(); - if (function == null) { - continue; - } - - if (getAllConstructors().contains(function) || - getAllInlinedConstructors().contains(function)) { - isConstructor = true; - continue; - } - - if (getAllDestructors().contains(function) || - getAllInlinedDestructors().contains(function)) { - isDestructor = true; - continue; - } - - // TODO: refactor to make this function and refactor method that uses - // it to use function instead of refiguring it out - referenceToIndeterminates.add(functionReference); + ReferencedClassObject referencedClassObject = + referenceToClassMap.get(referenceAddress); + Function function = referencedClassObject.getReferencedFunction(); + if (function == null) { + continue; } + if (getAllConstructors().contains(function) || + getAllInlinedConstructors().contains(function)) { + isConstructor = true; + continue; + } + + if (getAllDestructors().contains(function) || + getAllInlinedDestructors().contains(function)) { + isDestructor = true; + continue; + } + + // TODO: refactor to make this function and refactor method that uses + // it to use function instead of refiguring it out + referenceToIndeterminates.add(referenceAddress); + } // if one or more is a constructor and none are destructors then the indeterminate // inline is an inlined constructor if (isConstructor == true && isDestructor == false) { - processInlineConstructor(recoveredClass, inlineFunction, referenceToClassMap); + processInlineConstructor(recoveredClass, inlineFunction, + referenceToClassMap); + processedFunctions.add(inlineFunction); } // if one or more is a destructor and none are constructors then the indeterminate // inline is an inlined destructor else if (isConstructor == false && isDestructor == true) { processInlineDestructor(recoveredClass, inlineFunction, referenceToClassMap); + processedFunctions.add(inlineFunction); } else { @@ -6248,6 +6325,7 @@ public class RecoveredClassHelper { processInlineConstructor(recoveredClass, inlineFunction, referenceToClassMap); isConstructor = true; + processedFunctions.add(inlineFunction); } // inlined destructor @@ -6255,6 +6333,7 @@ public class RecoveredClassHelper { processInlineDestructor(recoveredClass, inlineFunction, referenceToClassMap); isDestructor = true; + processedFunctions.add(inlineFunction); } } @@ -6542,12 +6621,14 @@ public class RecoveredClassHelper { * 3. do not reference a vftable but call own destructor (call func on own c/d list) which * means it is just a deleting destructor for class but has no inlined destructor * @param recoveredClass the given class + * @param vfunction the given vfunction + * @param functionsWithVftableRef all class functions with vftable references * @throws CancelledException if cancelled * @throws DuplicateNameException if try to create same symbol name already in namespace * @throws InvalidInputException if issues setting return type */ private boolean processDeletingDestructor(RecoveredClass recoveredClass, Function vfunction, - List
allVftables) + Set functionsWithVftableRef) throws CancelledException, DuplicateNameException, InvalidInputException { // if the virtual function IS ALSO on the class constructor/destructor list @@ -6555,7 +6636,7 @@ public class RecoveredClassHelper { // determine if the inline is the class or parent/grandparent class destructor boolean isDeletingDestructor = false; - if (getAllClassFunctionsWithVtableRef(allVftables).contains(vfunction)) { + if (functionsWithVftableRef.contains(vfunction)) { recoveredClass.addDeletingDestructor(vfunction); recoveredClass.removeFromConstructorDestructorList(vfunction); @@ -6727,6 +6808,8 @@ public class RecoveredClassHelper { List recoveredClasses) throws CancelledException, InvalidInputException, DuplicateNameException, CircularDependencyException { + Set processedFunctions = new HashSet<>(); + for (RecoveredClass recoveredClass : recoveredClasses) { monitor.checkCancelled(); List indeterminateList = @@ -6741,16 +6824,27 @@ public class RecoveredClassHelper { for (Function indeterminateFunction : indeterminateList) { monitor.checkCancelled(); - // get the addresses in the function that refer to classes either by - // referencing a vftable in a class or by calling a function in a class - Map referenceToClassMap = - getReferenceToClassMap(recoveredClass, indeterminateFunction); + + // functions with inlines often contain multiple inlined constructor/destructors + // skip if already processed (processInline methods update all of them) + if (processedFunctions.contains(indeterminateFunction)) { + continue; + } + + Map referenceToClassMap = + functionReferenceMap.get(indeterminateFunction); + + if (referenceToClassMap == null) { + referenceToClassMap = + getReferenceToReferencedObjectsMap(recoveredClass, indeterminateFunction); + } List allDescendantConstructors = getAllDescendantConstructors(recoveredClass); if (allDescendantConstructors.contains(indeterminateFunction)) { processInlineConstructor(recoveredClass, indeterminateFunction, referenceToClassMap); + processedFunctions.add(indeterminateFunction); continue; } @@ -6759,6 +6853,7 @@ public class RecoveredClassHelper { if (allDescendantDestructors.contains(indeterminateFunction)) { processInlineDestructor(recoveredClass, indeterminateFunction, referenceToClassMap); + processedFunctions.add(indeterminateFunction); continue; } @@ -6787,12 +6882,14 @@ public class RecoveredClassHelper { if (ancestorConstructor != null) { processInlineConstructor(recoveredClass, indeterminateFunction, referenceToClassMap); + processedFunctions.add(indeterminateFunction); continue; } if (ancestorDestructor != null) { processInlineDestructor(recoveredClass, indeterminateFunction, referenceToClassMap); + processedFunctions.add(indeterminateFunction); continue; } @@ -6919,7 +7016,8 @@ public class RecoveredClassHelper { // process deleting destructors if type 1, 2 or 3 boolean isDeletingDestructor = - processDeletingDestructor(recoveredClass, vfunction, allVftables); + processDeletingDestructor(recoveredClass, vfunction, + allFunctionsThatRefVftables); if (isDeletingDestructor) { processPossibleDestructors(allPossibleCDs, possibleCalledDestructors, vfunction, allVftables); @@ -8748,4 +8846,103 @@ public class RecoveredClassHelper { return classStructureDataType; } + public class ReferencedClassObject { + + Address referenceLocation; + RecoveredClass containingClass; + Object referencedObject; + Function referencedFunction; + Address referencedAddress; + Boolean isConstructor; + Boolean isDestructor; + + public ReferencedClassObject(Address referenceLocation, Object referencedObject, + RecoveredClass containingClass) { + this.referenceLocation = referenceLocation; + this.referencedObject = referencedObject; + this.containingClass = containingClass; + + if (referencedObject instanceof Function function) { + referencedFunction = function; + referencedAddress = function.getEntryPoint(); + } + else if (referencedObject instanceof Address address) { + referencedAddress = address; + } + } + + /** + * Method to return the address of the reference location + * @return the address of the reference location + */ + public Address getReferenceLocation() { + return referenceLocation; + } + + /** + * Method to return the referenced address + * @return the referenced address or null if the referenced object is not an address + */ + public Address getReferencedAddress() { + return referencedAddress; + } + + /** + * Method to return the referenced function + * @return the referenced function or null if the referenced object is not a function + */ + public Function getReferencedFunction() { + return referencedFunction; + } + + /** + * Method to return the RecoveredClass that corresponds to the namespace that contains the + * given referenced object + * @return the RecoveredClass object for the namespace that contains the referenced object + */ + public RecoveredClass getContainingClass() { + return containingClass; + } + + /** + * Method to set whether the referenced object is a constructor or not + * @param choice should be set to true if it is a constructor and false if not + */ + public void setIsConstructor(Boolean choice) { + isConstructor = choice; + // Note that the opposite is not necessarily true + if (isConstructor) { + isDestructor = false; + } + } + + /** + * Method to return whether the referenced object is a constructor + * @return true if a constructor, false if not a constructor, null if unknown + */ + public Boolean isConstructor() { + return isConstructor; + } + + /** + * Method to set whether the referenced object is a destructor or not + * @param choice should be set to true if it is a destructor and false if not + */ + public void setIsDestructor(Boolean choice) { + isDestructor = choice; + // Note that the opposite is not necessarily true + if (isDestructor) { + isConstructor = false; + } + } + + /** + * Method to return whether the referenced object is a destructor + * @return true if a destructor, false if not a destructor, null if unknown + */ + public Boolean isDestructor() { + return isDestructor; + } + } + }