From 5709ca80d3444607edaf6a120a8eca6374eb2f11 Mon Sep 17 00:00:00 2001 From: ghidra007 Date: Tue, 29 Aug 2023 23:07:18 +0000 Subject: [PATCH] GP-3464 Improved gcc rtti script class struct recovery to better use dwarf info, split out parent struct in class struct, fix align issue keeping data from being created correctly, better id and name vftables in multi-inheritance case, clean up of exceptions, start of improved modeling vftables. --- .../classrecovery/RTTIClassRecoverer.java | 35 +- .../classrecovery/RTTIGccClassRecoverer.java | 452 +++++++++++------- .../classrecovery/RecoveredClassHelper.java | 93 +--- .../ghidra_scripts/classrecovery/Vftable.java | 95 ++++ .../ghidra_scripts/classrecovery/Vtable.java | 52 +- 5 files changed, 424 insertions(+), 303 deletions(-) create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/Vftable.java 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);