diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java index 49192fa0bf5..094fe04463f 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java @@ -150,28 +150,27 @@ public void retrieveExistingClassStructures(List recoveredClasse RecoveredClass recoveredClass = recoveredClassIterator.next(); // if class is non-virtual have to search for an existing class datatype - if (!recoveredClass.hasVftable()) { - DataType[] possibleExistingClassStructures = - extendedFlatAPI.getDataTypes(recoveredClass.getName()); - if (possibleExistingClassStructures.length == 0) { + + DataType[] possibleExistingClassStructures = + extendedFlatAPI.getDataTypes(recoveredClass.getName()); + if (possibleExistingClassStructures.length == 0) { + continue; + } + for (int i = 0; i < possibleExistingClassStructures.length; i++) { + monitor.checkCancelled(); + if (!(possibleExistingClassStructures[i] instanceof Structure)) { continue; } - for (int i = 0; i < possibleExistingClassStructures.length; i++) { - monitor.checkCancelled(); - if (!(possibleExistingClassStructures[i] instanceof Structure)) { - continue; - } - if (possibleExistingClassStructures[i].isNotYetDefined()) { - continue; - } - - Structure existingClassStructure = - (Structure) possibleExistingClassStructures[i]; - - recoveredClass.addExistingClassStructure(existingClassStructure); - break; + if (possibleExistingClassStructures[i].isNotYetDefined()) { + continue; } + + Structure existingClassStructure = (Structure) possibleExistingClassStructures[i]; + + recoveredClass.addExistingClassStructure(existingClassStructure); + break; } + //Iterate over constructor/destructor functions List constructorOrDestructorFunctions = recoveredClass.getConstructorOrDestructorFunctions(); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index 17d7bedabe8..94e164dd80b 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -17,14 +17,7 @@ package classrecovery; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import ghidra.app.cmd.label.DemanglerCmd; import ghidra.app.plugin.core.analysis.ReferenceAddressPair; @@ -33,67 +26,20 @@ import ghidra.app.util.demangler.DemanglerUtil; import ghidra.framework.plugintool.PluginTool; import ghidra.program.flatapi.FlatProgramAPI; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressIterator; -import ghidra.program.model.address.AddressOutOfBoundsException; -import ghidra.program.model.address.AddressRange; -import ghidra.program.model.address.AddressRangeIterator; -import ghidra.program.model.address.AddressSet; -import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.CharDataType; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeComponent; -import ghidra.program.model.data.DataTypeConflictHandler; -import ghidra.program.model.data.DataUtilities; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; import ghidra.program.model.data.DataUtilities.ClearDataMode; -import ghidra.program.model.data.InvalidDataTypeException; -import ghidra.program.model.data.LongDataType; -import ghidra.program.model.data.LongLongDataType; -import ghidra.program.model.data.Pointer; -import ghidra.program.model.data.PointerDataType; -import ghidra.program.model.data.PointerTypedef; -import ghidra.program.model.data.StringDataType; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.data.TerminatedStringDataType; -import ghidra.program.model.data.UnsignedIntegerDataType; import ghidra.program.model.lang.Register; -import ghidra.program.model.listing.Bookmark; -import ghidra.program.model.listing.BookmarkType; -import ghidra.program.model.listing.CircularDependencyException; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.FunctionManager; -import ghidra.program.model.listing.Instruction; -import ghidra.program.model.listing.InstructionIterator; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.DumbMemBufferImpl; -import ghidra.program.model.mem.MemBuffer; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; -import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.*; import ghidra.program.model.scalar.Scalar; -import ghidra.program.model.symbol.Namespace; -import ghidra.program.model.symbol.Reference; -import ghidra.program.model.symbol.ReferenceIterator; -import ghidra.program.model.symbol.ReferenceManager; -import ghidra.program.model.symbol.SourceType; -import ghidra.program.model.symbol.Symbol; -import ghidra.program.model.symbol.SymbolIterator; +import ghidra.program.model.symbol.*; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramMemoryUtil; import ghidra.util.Msg; -import ghidra.util.bytesearch.GenericByteSequencePattern; -import ghidra.util.bytesearch.GenericMatchAction; -import ghidra.util.bytesearch.Match; -import ghidra.util.bytesearch.MemoryBytePatternSearcher; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.InvalidInputException; +import ghidra.util.bytesearch.*; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class RTTIGccClassRecoverer extends RTTIClassRecoverer { @@ -150,6 +96,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { private Map> classToParentOffsetMap = new HashMap>(); + + private Map> classToVftableMap = new HashMap<>(); boolean isDwarfLoaded; boolean replaceClassStructs; @@ -253,7 +201,7 @@ public List createRecoveredClasses() throws CancelledException, updateClassesWithParentsAndFlags(typeinfos); Msg.debug(this, "Updating classes with vftables"); - updateClassWithVfunctions(recoveredClasses, vtables); + updateClassWithVfunctions(vtables); Msg.debug(this, "Creating Class Hierarchy lists and maps"); createClassHierarchyListAndMap(); @@ -274,10 +222,57 @@ public List createRecoveredClasses() throws CancelledException, Msg.debug(this, "Creating and Applying Class structures"); createAndApplyClassStructures(); + updateMultiVftableLabels(); + return recoveredClasses; } + //TODO: test with Windows and move to class helper + /** + * Method to update the labels of vftables that belong to classes with multiple vftables in + * order to distinguish which base class the vftable is for. + * @param recoveredClasses the list of RecoveredClass objects + * @throws CancelledException if cancelled + * @throws InvalidInputException if bad chars trying to label + * @throws DuplicateNameException if duplicate name + */ + private void updateMultiVftableLabels() + throws CancelledException, DuplicateNameException, InvalidInputException { + + if (recoveredClasses.isEmpty()) { + return; + } + for (RecoveredClass recoveredClass : recoveredClasses) { + monitor.checkCancelled(); + + // if there are no vftables or only one vftable in this class then there is no need to + // distinguish with a new label and can keep the generic one + List
vftableAddresses = recoveredClass.getVftableAddresses(); + if (vftableAddresses.size() < 2) { + continue; + } + + for (Address vftableAddress : vftableAddresses) { + RecoveredClass vftableBaseClass = + recoveredClass.getVftableBaseClass(vftableAddress); + if (vftableBaseClass != null) { + Symbol primarySymbol = symbolTable.getPrimarySymbol(vftableAddress); + + String baseClassName = vftableBaseClass.getName(); + // get simplified name by removing template + String shortenedTemplateName = vftableBaseClass.getShortenedTemplateName(); + if (!shortenedTemplateName.isBlank()) { + baseClassName = shortenedTemplateName; + } + + primarySymbol.setName("vftable_for_" + baseClassName, + primarySymbol.getSource()); + } + } + } + } + private List createSpecialTypeinfos() throws CancelledException { List specialGccTypeinfos = new ArrayList(); @@ -638,7 +633,6 @@ List getSpecialTypeinfoSymbols() { return symbols; } - // TODO: split out methods private void updateClassesWithParentsAndFlags(List typeinfos) throws CancelledException { @@ -646,7 +640,6 @@ private void updateClassesWithParentsAndFlags(List typeinfos) for (GccTypeinfo typeinfo : typeinfos) { monitor.checkCancelled(); - Address typeinfoAddress = typeinfo.getAddress(); // skip the typeinfo symbols from the three special typeinfos if (typeinfo.isSpecialTypeinfo()) { @@ -658,25 +651,14 @@ private void updateClassesWithParentsAndFlags(List typeinfos) RecoveredClass recoveredClass = getClass(classNamespace); if (recoveredClass == null) { - throw new IllegalArgumentException( + Msg.error(this, "RecoveredClass should already exist for " + classNamespace.getName(true)); + continue; } + // per docs those on this list are public and have no-inherited classes if (typeinfo.isClassTypeinfo()) { - recoveredClass.setHasSingleInheritance(true); - recoveredClass.setHasMultipleInheritance(false); - recoveredClass.setHasMultipleVirtualInheritance(false); - recoveredClass.setInheritsVirtualAncestor(false); - - // no parents so just add empty order and parent maps to the class maps - Map orderToParentMap = - new HashMap(); - - classToParentOrderMap.put(recoveredClass, orderToParentMap); - - Map parentToOffsetMap = new HashMap(); - - classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); + addClassFlagsForNoInheritanceClass(recoveredClass); continue; } @@ -684,46 +666,16 @@ private void updateClassesWithParentsAndFlags(List typeinfos) // classes containing only a single, public, non-virtual base at offset zero if (typeinfo.isSiClassTypeinfo()) { - List baseTypeinfos = typeinfo.getBaseTypeinfos(); - if (baseTypeinfos.size() != 1) { - throw new IllegalArgumentException( - "SiClassTypeinfo " + classNamespace.getName(true) + - " should have exactly one parent"); - } - - GccTypeinfo siParentTypeinfo = baseTypeinfos.get(0).getBaseTypeinfo(); - RecoveredClass parentClass = getClass(siParentTypeinfo.getNamespace()); + RecoveredClass parentClass = + addClassParentAndFlagsForSiClass(recoveredClass, typeinfo); - // parent isn't a known class - possibly is an external parent + // parent isn't a known class - cannot continue processing recoveredClass if (parentClass == null) { - throw new IllegalArgumentException( - "RecoveredClass should already exist for " + - siParentTypeinfo.getNamespace().getName(true)); - } - - updateClassWithParent(parentClass, recoveredClass); - recoveredClass.setHasSingleInheritance(true); - recoveredClass.setHasMultipleInheritance(false); - recoveredClass.setHasMultipleVirtualInheritance(false); - parentClass.setIsPublicClass(true); - recoveredClass.addParentToBaseTypeMapping(parentClass, false); - - // TODO: make method to check all ancestors not just parent - if (siParentTypeinfo.isVmiClassTypeinfo()) { - recoveredClass.setInheritsVirtualAncestor(true); + Msg.error(this, + "Removing class: " + recoveredClass.getName() + " from list to" + + " process since parent information is not availalbe."); + recoveredClasses.remove(recoveredClass); } - - // add order to parent and parent offset - Map orderToParentMap = - new HashMap(); - orderToParentMap.put(0, parentClass); - classToParentOrderMap.put(recoveredClass, orderToParentMap); - - Map parentToOffsetMap = new HashMap(); - parentToOffsetMap.put(parentClass, 0L); - - classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); - continue; } @@ -732,12 +684,11 @@ private void updateClassesWithParentsAndFlags(List typeinfos) List parents = addClassParentsAndFlagsForVmiClass(recoveredClass, typeinfo); - if (parents.isEmpty()) { + if (parents == null) { Msg.debug(this, - "Could not get vmi parent from typeinfoAddress - removing class from list" + - typeinfoAddress.toString()); + "Removing class: " + recoveredClass.getName() + " from list to" + + " process since full parent information is not availalbe."); recoveredClasses.remove(recoveredClass); - continue; } } @@ -1496,7 +1447,7 @@ private Map getVtablesUsingTypeinfo(GccTypeinfo typeinf monitor.checkCancelled(); // get top of vtable - Address vtableAddress = getPrimaryVtableAddress(typeinfoRef); + Address vtableAddress = getPrimaryVtableAddress(typeinfoRef, typeinfo); // no vtable associated with this typeinfo if (vtableAddress == null) { @@ -1706,7 +1657,8 @@ List getTypeinfosByType(List typeinfoSymbols, String typeinfoTyp return subsetByType; } - private Address getPrimaryVtableAddress(Address typeinfoRef) throws CancelledException { + private Address getPrimaryVtableAddress(Address typeinfoRef, GccTypeinfo typeinfo) + throws CancelledException { if (typeinfoRef == null) { return null; @@ -1747,6 +1699,15 @@ private Address getPrimaryVtableAddress(Address typeinfoRef) throws CancelledExc return null; } + Address vtableAddress = offsetToTop; + + // NEW CHECK: check to see if referenced typeinfo is non-inheritance type and if so + // return the current value of vtableAddress as there is only one long (zeres) for + // vtables referencing non-inh class typeinfos + if (typeinfo.isClassTypeinfo()) { + return vtableAddress; + } + // start with last verified part of possible vtable and continue going backwards // until top of vtable is found // stop if top of mem block @@ -1754,7 +1715,7 @@ private Address getPrimaryVtableAddress(Address typeinfoRef) throws CancelledExc // stop if referenced // are they ever zero - not that i have seen so far in the last vftable // if pointer to something or valid address or is in a structure - Address vtableAddress = offsetToTop; + MemoryBlock currentBlock = program.getMemory().getBlock(typeinfoRef); while (vtableAddress != null) { @@ -2527,8 +2488,7 @@ private void updateTypeinfosWithBases(List typeinfos, } - private boolean updateSiTypeinfo(GccTypeinfo typeinfo, Map typeinfoMap) - throws CancelledException { + private boolean updateSiTypeinfo(GccTypeinfo typeinfo, Map typeinfoMap) { Data siTypeinfoStructure = listing.getDataAt(typeinfo.getAddress()); @@ -2871,11 +2831,9 @@ private Symbol createDemangledTypeinfoSymbol(Address typeinfoAddress) // get the newly created symbol to get the namespace Symbol typeinfoNameSymbol = symbolTable.getPrimarySymbol(typeinfoNameAddress); - // TODO: need to account for rare case where there are more than one typeinfos - // with + // need to account for rare case where there are more than one typeinfos with // exact same class and name so make two classes in this case - name second one // dupe# - // TODO: instead/also - eliminate the ones with no refs or just pick one? List symbols = symbolTable.getSymbols(typeinfoNameSymbol.getName(), typeinfoNameSymbol.getParentNamespace()); if (symbols.size() > 1) { @@ -2915,6 +2873,7 @@ private Symbol createDemangledTypeinfoSymbol(Address typeinfoAddress) // create the new typeinfo symbol in the demangled namespace Symbol newSymbol = symbolTable.createLabel(typeinfoAddress, "typeinfo", classNamespace, SourceType.ANALYSIS); + newSymbol.setPrimary(); api.setPlateComment(typeinfoAddress, "typeinfo for " + classNamespace.getName(true)); @@ -3482,15 +3441,72 @@ private StructureDataType createVmiClassTypeInfoStructure( return vmiClassTypeInfoStructure; } + private void addClassFlagsForNoInheritanceClass(RecoveredClass recoveredClass) { + recoveredClass.setHasSingleInheritance(true); + recoveredClass.setHasMultipleInheritance(false); + recoveredClass.setHasMultipleVirtualInheritance(false); + recoveredClass.setInheritsVirtualAncestor(false); + + // no parents so just add empty order and parent maps to the class maps + Map orderToParentMap = new HashMap(); + + classToParentOrderMap.put(recoveredClass, orderToParentMap); + + Map parentToOffsetMap = new HashMap(); + + classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); + } + + private RecoveredClass addClassParentAndFlagsForSiClass(RecoveredClass recoveredClass, + GccTypeinfo typeinfo) { + List baseTypeinfos = typeinfo.getBaseTypeinfos(); + if (baseTypeinfos.size() != 1) { + throw new IllegalArgumentException( + "SiClassTypeinfo " + recoveredClass.getClassNamespace().getName(true) + + " should have exactly one parent"); + } + + GccTypeinfo siParentTypeinfo = baseTypeinfos.get(0).getBaseTypeinfo(); + + RecoveredClass parentClass = getClass(siParentTypeinfo.getNamespace()); + if (parentClass == null) { + Msg.error(this, "Parent class's RecoveredClass object for : " + + siParentTypeinfo.getNamespace().getName(true) + " should already exist. "); + return null; + } + + updateClassWithParent(parentClass, recoveredClass); + recoveredClass.setHasSingleInheritance(true); + recoveredClass.setHasMultipleInheritance(false); + recoveredClass.setHasMultipleVirtualInheritance(false); + parentClass.setIsPublicClass(true); + recoveredClass.addParentToBaseTypeMapping(parentClass, false); + + // NEW check to get all ancestor info + if (typeinfo.getNumAllVirtualBases() > 0) { + recoveredClass.setInheritsVirtualAncestor(true); + } + + // add order to parent and parent offset + Map orderToParentMap = new HashMap(); + orderToParentMap.put(0, parentClass); + classToParentOrderMap.put(recoveredClass, orderToParentMap); + + Map parentToOffsetMap = new HashMap(); + parentToOffsetMap.put(parentClass, 0L); + + classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); + return parentClass; + + } + /** * Method to add parents to the given class * * @param recoveredClass the given class - * @param typeinfoAddress the address of the typeinfo + * @param typeinfo the the GccTypeinfo object for this class * @return list of parents for the given class - * @throws CancelledException - * @throws Exception if cannot access the given typeinfo structure, one - * of its components, or it is not a vmi structure + * @throws CancelledException if cancelled */ private List addClassParentsAndFlagsForVmiClass(RecoveredClass recoveredClass, GccTypeinfo typeinfo) @@ -3523,6 +3539,7 @@ private List addClassParentsAndFlagsForVmiClass(RecoveredClass r boolean hasVirtualInheritance = false; if (typeinfo.getNumAllVirtualBases() > 0) { + recoveredClass.setInheritsVirtualAncestor(true); hasVirtualInheritance = true; } @@ -3547,9 +3564,11 @@ private List addClassParentsAndFlagsForVmiClass(RecoveredClass r RecoveredClass parentClass = getClass(vmiParentTypeinfo.getNamespace()); if (parentClass == null) { - throw new IllegalArgumentException( - "RecoveredClass should already exist for " + - vmiParentTypeinfo.getNamespace().getName(true)); + + Msg.error(this, "Parent class's RecoveredClass object for : " + + vmiParentTypeinfo.getNamespace().getName(true) + " should already exist. "); + return null; + } updateClassWithParent(parentClass, recoveredClass); @@ -3966,37 +3985,100 @@ private void createClassesFromTypeinfos(List typeinfos) throws Canc } } - private void updateClassWithVfunctions(List recoveredClasses, - List vtables) - throws Exception { + private void updateClassWithVfunctions(List vtables) throws Exception { - for (RecoveredClass recoveredClass : recoveredClasses) { + for (Vtable vtable : vtables) { monitor.checkCancelled(); - Namespace classNamespace = recoveredClass.getClassNamespace(); - Vtable classVtable = getVtable(vtables, classNamespace); + Namespace classNamespace = vtable.getNamespace(); - if (classVtable == null) { - Msg.debug(this, "No vtable for class: " + classNamespace.getName()); - return; + RecoveredClass recoveredClass = getClass(classNamespace); + + if (recoveredClass == null) { + Msg.error(this, "No class for vtable : " + classNamespace.getName()); + continue; } - Address vftableAddress = classVtable.getVfunctionTop(); + Address vftableAddress = vtable.getVfunctionTop(); if (vftableAddress == null) { - return; + continue; } - Symbol vftableSymbol = symbolTable.getPrimarySymbol(vftableAddress); + // get the number of bases + // if only one then check no internal vtables + // if more than one then check matches num internal vtables + // match up the offset with the correct internal vftaable + List internalVtables = vtable.getInternalVtables(); + List
vftableAddresses = new ArrayList
(); + vftableAddresses.add(vftableAddress); + for (Vtable internalVtable : internalVtables) { + monitor.checkCancelled(); + + Address vfunctionTop = internalVtable.getVfunctionTop(); + if (vfunctionTop == null) { + continue; + } + vftableAddresses.add(vfunctionTop); + } + Collections.sort(vftableAddresses); - updateClassWithVftable(recoveredClass, vftableSymbol, true, false); + List baseOffsets = new ArrayList(); + GccTypeinfo referencedTypeinfo = vtable.getReferencedTypeinfo(); + List baseTypeinfos = referencedTypeinfo.getBaseTypeinfos(); + List allBaseTypeinfos = referencedTypeinfo.getAllBaseTypeinfos(); + + // handle no parent case + if (baseTypeinfos.size() == 0) { + Symbol vftableSymbol = symbolTable.getPrimarySymbol(vftableAddress); + updateClassWithVftable(recoveredClass, vftableSymbol, true, false); + recoveredClass.addClassOffsetToVftableMapping(0, vftableAddress); + continue; + } - // For now just assume simple case - // will have to refactor helper to handle gcc better - recoveredClass.addClassOffsetToVftableMapping(0, vftableAddress); + // handle parent case + for (BaseTypeinfo baseTypeinfo : baseTypeinfos) { + monitor.checkCancelled(); + baseOffsets.add(baseTypeinfo.getOffset()); + } + + + if (baseOffsets.size() != vftableAddresses.size()) { + + Msg.debug(this, + classNamespace.getName(true) + " different size offset list and vftable list"); + + if (allBaseTypeinfos.size() == vftableAddresses.size()) { + Msg.debug(this, "Same number of total bases and vftables though"); + } + continue; + } + + for (int i = 0; i < baseOffsets.size(); i++) { + + BaseTypeinfo baseTypeinfo = baseTypeinfos.get(i); + long offset = baseOffsets.get(i); + + Address correspondingVftableAddress = vftableAddresses.get(i); + Symbol vftableSymbol = symbolTable.getPrimarySymbol(correspondingVftableAddress); + + updateClassWithVftable(recoveredClass, vftableSymbol, true, false); + recoveredClass.addClassOffsetToVftableMapping((int) offset, + correspondingVftableAddress); + + Namespace namespace = baseTypeinfo.getBaseTypeinfo().getNamespace(); + RecoveredClass baseClass = getClass(namespace); + if (baseClass == null) { + continue; + } + recoveredClass.addVftableToBaseClassMapping(correspondingVftableAddress, baseClass); + + //TODO: populate the new Vftable objects + } } } + /** * Use information from RTTI Base class Arrays to create class hierarchy lists * and maps @@ -4426,9 +4508,12 @@ private void processDataTypes(RecoveredClass recoveredClass) } if (!recoveredClass.hasVftable()) { - createSimpleClassStructure(recoveredClass, null); - // return in this case because if there is no vftable for a class the script - // cannot + Structure classStructure = createSimpleClassStructure(recoveredClass, null); + if (classStructure == null) { + Msg.error(this, "Could not create class structure for " + + recoveredClass.getClassNamespace().getName(true)); + } + // return here because if there is no vftable for a class the script cannot // identify any member functions so there is no need to process the rest of this // method return; @@ -4443,6 +4528,11 @@ private void processDataTypes(RecoveredClass recoveredClass) // data // structures, and class member data structure Structure classStruct = createSimpleClassStructure(recoveredClass, vfPointerDataTypes); + if (classStruct == null) { + Msg.error(this, "Could not create class structure or continue processing for " + + recoveredClass.getClassNamespace().getName(true)); + return; + } // Now that we have a class data type // name constructor and destructor functions and put into the class namespace @@ -4479,8 +4569,7 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, CategoryPath classPath = recoveredClass.getClassPath(); // get either existing structure if prog has a structure created by pdb or - // computed - // structure from decompiled construtor(s) info + // computed structure from decompiled construtor(s) info Structure classStructure; if (recoveredClass.hasExistingClassStructure()) { classStructure = recoveredClass.getExistingClassStructure(); @@ -4490,8 +4579,15 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, } int structLen = 0; + // for gcc need to get actual length of components not the aligned length if (classStructure != null) { - structLen = addAlignment(classStructure.getLength()); + + int numComponents = classStructure.getNumComponents(); + classStructure.getNumDefinedComponents(); + + DataTypeComponent lastComponent = classStructure.getComponent(numComponents - 1); + structLen = lastComponent.getOffset() + lastComponent.getLength(); + } Structure classStructureDataType = @@ -4501,8 +4597,7 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, if (nonInheritedClasses.contains(recoveredClass) && vfPointerDataTypes != null) { // the size was checked before calling this method so we know there is one and - // only - // one for this simple case + // only one for this simple case Address vftableAddress = recoveredClass.getVftableAddresses().get(0); DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress); @@ -4513,24 +4608,25 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, CLASS_VTABLE_PTR_FIELD_EXT, monitor); } // if single inheritance or multi non-virtual (wouldn't have called this method - // if - // it were virtually inherited) put parent struct and data into class struct + // if it were virtually inherited) put parent struct and data into class struct else { Map orderToParentMap = classToParentOrderMap.get(recoveredClass); if (orderToParentMap == null || orderToParentMap.isEmpty()) { - throw new Exception( + Msg.error(this, "Vmi class " + recoveredClass.getClassNamespace().getName(true) + " should have a parent in the classToParentOrderMap but doesn't"); + return null; } Map parentToOffsetMap = classToParentOffsetMap.get(recoveredClass); if (parentToOffsetMap.isEmpty()) { - throw new Exception( + Msg.error(this, "Vmi class " + recoveredClass.getClassNamespace().getName(true) + " should have a parent in the classToParentOffsetMap but doesn't"); + return null; } int numParents = orderToParentMap.keySet().size(); @@ -4539,30 +4635,34 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, Long parentOffsetLong = parentToOffsetMap.get(parent); if (parentOffsetLong == null) { - throw new Exception( + Msg.error(this, "Can't get parent offset for " + parent.getClassNamespace().getName(true)); + return null; } int parentOffset = parentOffsetLong.intValue(); Structure baseClassStructure = getClassStructureFromDataTypeManager(parent); - // if we can't get the parent throw exception because it shouldn't get here if - // the - // parent doesn't exist + if (baseClassStructure == null) { - throw new Exception( + Msg.error(this, "Parent structure: " + parent.getClassNamespace().getName(true) + - " : structure should exist but doesn't."); + " should exist but doesn't."); + return null; } - // if it fits at offset or is at the end and class structure can be grown, // copy the whole baseClass structure to the class Structure at the given offset EditStructureUtils.addDataTypeToStructure(classStructureDataType, parentOffset, - baseClassStructure, - baseClassStructure.getName(), monitor); + baseClassStructure, baseClassStructure.getName(), monitor); + } } + // for gcc need to split out parent parts before figuring out data length + // to avoid internal alignment issues causing the wrong size + classStructureDataType = + addClassVftables(classStructureDataType, recoveredClass, vfPointerDataTypes); + // figure out class data, if any, create it and add to class structure int dataOffset = getDataOffset(recoveredClass, classStructureDataType); int dataLen = UNKNOWN; @@ -4588,11 +4688,7 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, } - classStructureDataType = - addClassVftables(classStructureDataType, recoveredClass, vfPointerDataTypes); - - // unused at this point until something figures out how to create them and where - // to + // unused at this point until something figures out how to create them and where to // put them classStructureDataType = addVbtableToClassStructure(recoveredClass, classStructureDataType, true); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index b4aaaaafb08..d64ba2e946c 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -16,17 +16,7 @@ //DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. package classrecovery; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import ghidra.app.cmd.function.ApplyFunctionSignatureCmd; @@ -40,78 +30,21 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.flatapi.FlatProgramAPI; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressOutOfBoundsException; -import ghidra.program.model.address.AddressRange; -import ghidra.program.model.address.AddressRangeIterator; -import ghidra.program.model.address.AddressSet; -import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.address.GlobalNamespace; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.Category; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeComponent; -import ghidra.program.model.data.DataTypeConflictHandler; -import ghidra.program.model.data.DataTypeDependencyException; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FunctionDefinition; -import ghidra.program.model.data.FunctionDefinitionDataType; -import ghidra.program.model.data.NoisyStructureBuilder; -import ghidra.program.model.data.ParameterDefinition; -import ghidra.program.model.data.Pointer; -import ghidra.program.model.data.PointerDataType; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.data.Undefined1DataType; -import ghidra.program.model.data.Undefined4DataType; -import ghidra.program.model.data.Undefined8DataType; -import ghidra.program.model.data.VoidDataType; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; import ghidra.program.model.lang.CompilerSpec; -import ghidra.program.model.listing.Bookmark; -import ghidra.program.model.listing.BookmarkManager; -import ghidra.program.model.listing.BookmarkType; -import ghidra.program.model.listing.CircularDependencyException; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.FlowOverride; -import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.*; import ghidra.program.model.listing.Function.FunctionUpdateType; -import ghidra.program.model.listing.FunctionManager; -import ghidra.program.model.listing.FunctionSignature; -import ghidra.program.model.listing.Instruction; -import ghidra.program.model.listing.InstructionIterator; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Parameter; -import ghidra.program.model.listing.Program; -import ghidra.program.model.listing.ReturnParameterImpl; import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.pcode.HighFunction; -import ghidra.program.model.pcode.HighVariable; -import ghidra.program.model.pcode.PcodeOp; -import ghidra.program.model.pcode.PcodeOpAST; -import ghidra.program.model.pcode.Varnode; -import ghidra.program.model.symbol.Namespace; -import ghidra.program.model.symbol.RefType; -import ghidra.program.model.symbol.Reference; -import ghidra.program.model.symbol.ReferenceIterator; -import ghidra.program.model.symbol.ReferenceManager; -import ghidra.program.model.symbol.SourceType; -import ghidra.program.model.symbol.Symbol; -import ghidra.program.model.symbol.SymbolIterator; -import ghidra.program.model.symbol.SymbolTable; -import ghidra.program.model.symbol.SymbolType; +import ghidra.program.model.pcode.*; +import ghidra.program.model.symbol.*; import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramMemoryUtil; import ghidra.util.InvalidNameException; import ghidra.util.Msg; -import ghidra.util.bytesearch.GenericByteSequencePattern; -import ghidra.util.bytesearch.GenericMatchAction; -import ghidra.util.bytesearch.Match; -import ghidra.util.bytesearch.MemoryBytePatternSearcher; +import ghidra.util.bytesearch.*; import ghidra.util.datastruct.ListAccumulator; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.InvalidInputException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class RecoveredClassHelper { @@ -4490,7 +4423,7 @@ public void createClassStructureWhenNoParentOrVftable(RecoveredClass recoveredCl recoveredClass.getName(), structLen, dataTypeManager); int numComponents = computedClassDataStructure.getNumDefinedComponents(); - for (int i = 1; i < numComponents; i++) { + for (int i = 0; i < numComponents; i++) { monitor.checkCancelled(); DataTypeComponent component = computedClassDataStructure.getComponent(i); int offset = component.getOffset(); @@ -7711,7 +7644,9 @@ public List
getVftablesContaining(Function function) throws CancelledEx Address[] functionThunkAddresses = function.getFunctionThunkAddresses(true); - List
functionAddresses = new ArrayList<>(); + // add any thunk addresses to the list + List
functionAddresses = new ArrayList
(); + // add the function itself to the list functionAddresses.add(function.getEntryPoint()); if (functionThunkAddresses != null) { @@ -7719,6 +7654,10 @@ public List
getVftablesContaining(Function function) throws CancelledEx functionAddresses.addAll(Arrays.asList(functionThunkAddresses)); } + if (functionThunkAddresses != null) { + functionAddresses.addAll(Arrays.asList(functionThunkAddresses)); + } + for (Address address : functionAddresses) { monitor.checkCancelled(); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vftable.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vftable.java new file mode 100644 index 00000000000..7bcf7634576 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vftable.java @@ -0,0 +1,95 @@ +/* ### + * 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 classrecovery; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.program.model.address.Address; +import ghidra.program.model.symbol.Namespace; + +public class Vftable { + + private Address vftableAddress; + private Namespace namespace; + private Vtable vtable; + private RecoveredClass containingClass; + private Long classOffset; + private RecoveredClass associatedClass; + private boolean isInternal; + private List
vfunctions = new ArrayList
(); + + public Vftable(Address vttAddress, Namespace namespace, Vtable vtable, boolean isInternal) { + this.vftableAddress = vttAddress; + this.namespace = namespace; + this.vtable = vtable; + this.isInternal = isInternal; + } + + public Address getAddress() { + return vftableAddress; + } + + public Namespace getNamespace() { + return namespace; + } + + public Vtable getAssociatedVtable() { + return vtable; + } + + public boolean isInternal() { + return isInternal; + + } + + public boolean isPrimary() { + return !isInternal; + } + + public void setContainingClass(RecoveredClass recoveredClass) { + containingClass = recoveredClass; + } + + public RecoveredClass getContainingClass() { + return containingClass; + } + + public void setOffset(Long offset) { + classOffset = offset; + } + + public Long getClassOffset() { + return classOffset; + } + + public void setAssociatedClass(RecoveredClass recoveredClass) { + associatedClass = recoveredClass; + } + + public RecoveredClass getAssociatedClass() { + return associatedClass; + } + + public void addVfunction(Address address) { + vfunctions.add(address); + } + + public int getNumVfunctions() { + return vfunctions.size(); + } + //TODO: return num non-null vfunctions +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vtable.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vtable.java index 78030c76db3..e2ad612628e 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vtable.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vtable.java @@ -18,32 +18,12 @@ import java.util.ArrayList; import java.util.List; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressOutOfBoundsException; -import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.address.AddressSpace; -import ghidra.program.model.address.GlobalNamespace; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.LongDataType; -import ghidra.program.model.data.LongLongDataType; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; import ghidra.program.model.lang.Register; -import ghidra.program.model.listing.Bookmark; -import ghidra.program.model.listing.BookmarkType; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.FunctionManager; -import ghidra.program.model.listing.Instruction; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.symbol.Namespace; -import ghidra.program.model.symbol.SourceType; -import ghidra.program.model.symbol.Symbol; -import ghidra.program.model.symbol.SymbolTable; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.*; +import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -152,8 +132,6 @@ protected void setup() throws CancelledException { return; } - // setIsConstructionVtable(); - if (!isValid) { return; } @@ -227,7 +205,6 @@ public GccTypeinfo getReferencedTypeinfo() { protected void setTypeinfoAddress() { - GccTypeinfo typeinfo = getReferencedTypeinfo(); typeinfoAddress = typeinfo.getAddress(); typeinfoNamespace = typeinfo.getNamespace(); @@ -303,7 +280,6 @@ public Integer getNumVfunctions() { protected void setHasVfunctions() throws CancelledException { - Address typeinfoRefAddress = getTypeinfoRefAddress(); if (isPrimary == null) { isValid = false; return; @@ -539,6 +515,22 @@ public int getLength() { return length; } + public List getOffsets() { + + List offsetValues = new ArrayList(); + offsetValues.add(topOffsetValue); + + // skip to next offset above the first one which we already knew and have added + // add all the values until all including the value at the vtableAddress are added + Address nextOffsetAddress = typeinfoRefAddress.subtract(2 * defaultPointerSize); + while (nextOffsetAddress.getOffset() <= vtableAddress.getOffset()) { + offsetValues.add(extendedFlatAPI.getLongValueAt(nextOffsetAddress)); + nextOffsetAddress = nextOffsetAddress.subtract(defaultPointerSize); + } + + return offsetValues; + } + private void findInternalVtables() throws CancelledException { // if the current table is already an internal vtable there won't be any @@ -725,7 +717,7 @@ protected boolean applyVtableData() throws CancelledException, Exception { } public Data createVftableArray(Address vftableAddress, int numFunctionPointers) - throws CancelledException, AddressOutOfBoundsException { + throws AddressOutOfBoundsException { listing.clearCodeUnits(vftableAddress, vftableAddress.add((numFunctionPointers * defaultPointerSize - 1)), false);