diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java index 5e4078d6904..7eb439c9673 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -163,6 +163,11 @@ public void run() throws Exception { } else if (isGcc()) { + boolean runGcc = askYesNo("GCC Class Recovery Still Under Development", + "I understand that gcc class recovery is still under development and my results will be incomplete but want to run this anyway."); + if (!runGcc) { + return; + } nameVfunctions = true; recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, @@ -545,25 +550,6 @@ private boolean hasConstructorDestructorDiscrepancy(RecoveredClass recoveredClas return true; } - - - //TODO: call this before create data in debug mode from script - private void findClassesWithErrors(List recoveredClasses) - throws CancelledException { - - Iterator iterator = recoveredClasses.iterator(); - while (iterator.hasNext()) { - monitor.checkCanceled(); - RecoveredClass recoveredClass = iterator.next(); - if (hasConstructorDestructorDiscrepancy(recoveredClass)) { - println(recoveredClass.getName() + " has function on both c and d lists"); - } - } - } - - - - /** * Method to analyze the program changes with the decompiler parameter ID analyzer * @param set the set of addresses to analyze diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java index 68c7f69066c..b06ec50c80b 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java @@ -131,25 +131,25 @@ public List createRecoveredClasses() { /** * Method to promote the namespace is a class namespace. - * @param vftableNamespace the namespace for the vftable + * @param namespace the namespace for the vftable * @return true if namespace is (now) a class namespace or false if it could not be promoted. */ - public Namespace promoteToClassNamespace(Namespace vftableNamespace) { + public Namespace promoteToClassNamespace(Namespace namespace) { try { - Namespace newClass = NamespaceUtils.convertNamespaceToClass(vftableNamespace); + Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace); SymbolType symbolType = newClass.getSymbol().getSymbolType(); if (symbolType == SymbolType.CLASS) { return newClass; } Msg.debug(this, - "Could not promote " + vftableNamespace.getName() + " to a class namespace"); + "Could not promote " + namespace.getName() + " to a class namespace"); return null; } catch (InvalidInputException e) { - Msg.debug(this, "Could not promote " + vftableNamespace.getName() + + Msg.debug(this, "Could not promote " + namespace.getName() + " to a class namespace because " + e.getMessage()); return null; } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index 85e3835bd48..df0405d5d1f 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -14,6 +14,7 @@ * limitations under the License. */ package classrecovery; + /* ### * IP: GHIDRA * @@ -34,18 +35,18 @@ import java.util.stream.Collectors; import ghidra.app.cmd.label.DemanglerCmd; +import ghidra.app.util.demangler.*; +import ghidra.framework.options.Options; import ghidra.framework.plugintool.PluginTool; import ghidra.program.flatapi.FlatProgramAPI; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; -import ghidra.program.model.mem.MemoryAccessException; -import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.mem.*; import ghidra.program.model.symbol.*; import ghidra.program.util.ProgramLocation; import ghidra.util.Msg; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.InvalidInputException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class RTTIGccClassRecoverer extends RTTIClassRecoverer { @@ -70,8 +71,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVfunctions, - TaskMonitor monitor) { + boolean nameVfunctions, TaskMonitor monitor) { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, monitor); @@ -98,7 +98,6 @@ public boolean isValidProgramType() { @Override public List createRecoveredClasses() { - try { List recoveredClasses = processGccRTTI(); @@ -111,7 +110,11 @@ public List createRecoveredClasses() { createClassHierarchyListAndMapForGcc(recoveredClasses); + //TODO: check for dwarf + //boolean isDwarfLoaded = isDwarfLoaded(); assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); + //else + // find c/ds other way createVftableOrderMap(recoveredClasses); @@ -132,8 +135,6 @@ public List createRecoveredClasses() { return null; } - - } private boolean isGcc() { @@ -163,8 +164,7 @@ private boolean isGcc() { byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; Address found = program.getMemory().findBytes(commentBlock.getStart(), - commentBlock.getEnd(), - gccBytes, maskBytes, true, monitor); + commentBlock.getEnd(), gccBytes, maskBytes, true, monitor); if (found == null) { return false; } @@ -234,7 +234,6 @@ private Address findSpecialVtable(String namespace, String name) throws Cancelle private List processGccRTTI() throws CancelledException, Exception { - // create rtti vtables and typeinfo structs // find the three special vtables and replace the incorrectly made array with // data types found in vtable @@ -266,7 +265,15 @@ private List processGccRTTI() throws CancelledException, Excepti } } - // TODO: are all recovered classes in the map? + // check map for classes that are not in the recoveredClass list + Set keySet = namespaceToClassMap.keySet(); + for (Namespace namespace : keySet) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = namespaceToClassMap.get(namespace); + if (!recoveredClasses.contains(recoveredClass)) { + recoveredClasses.add(recoveredClass); + } + } // update the recoveredClass list with the typeinfo classes that do not have vtables Set typeinfoClasses = classToTypeinfoMap.keySet(); @@ -308,9 +315,14 @@ private List processGccRTTI() throws CancelledException, Excepti if (DEBUG) { Msg.debug(this, "Shouldn't be a null class here: " + classNamespace.getName()); } - recoveredClass = addNoVftableClass(classNamespace); + recoveredClass = createNewClass(classNamespace, false); recoveredClasses.add(recoveredClass); } + else { + if (!recoveredClasses.contains(recoveredClass)) { + recoveredClasses.add(recoveredClass); + } + } Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { @@ -392,21 +404,22 @@ private List processGccRTTI() throws CancelledException, Excepti recoveredClass.setHasMultipleInheritance(true); - if (recoveredClass.inheritsVirtualAncestor()) { recoveredClass.setHasMultipleVirtualInheritance(true); } List parents = addGccClassParents(recoveredClass, typeinfoAddress); - - if (!recoveredClasses.containsAll(parents)) { - if (DEBUG) { - Msg.debug(this, - "missing some parents from " + recoveredClass.getName() + " on list"); + if (parents.isEmpty()) { + continue; + } + for (RecoveredClass parent : parents) { + monitor.checkCanceled(); + if (!recoveredClasses.contains(parent)) { + Msg.debug(this, "adding parent " + parent.getName() + " to list."); + recoveredClasses.add(parent); } } -// newNonVftableClasses = -// addMissingClasses(parents, newNonVftableClasses, recoveredClasses); + } } @@ -417,7 +430,7 @@ private List processGccRTTI() throws CancelledException, Excepti RecoveredClass recoveredClass = recoveredClassIterator.next(); - Msg.debug(this, "Processing class " + recoveredClass.getName()); + Msg.debug(this, "Processing class " + recoveredClass.getClassNamespace().getName(true)); List
vftableAddresses = recoveredClass.getVftableAddresses(); @@ -443,21 +456,8 @@ private List processGccRTTI() throws CancelledException, Excepti private void createGccRttiData() throws CancelledException, Exception { - // TODO: find typeinfo's using other means besides their names since in static case they - // aren't named - // TODO: either check for DWARF and do below if dwarf and another method to find them first - // if not dwarf or combine and either way they are found - immediately call the create typeinfo struct - // but for one at a time. - // remove the ones in the list that are in the EXTERNAL (space or any non-loaded sapce) - // but keep the special ones on symbol list or maybe removet hem here too then don't have - // to keep skipping them later - - // find all typeinfo symbols and get their class namespace and create RecoveredClass object - List typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet( - program.getAddressFactory().getAddressSet(), "typeinfo", true); - // create the appropriate type of type info struct at the various typeinfo symbol locations - createTypeinfoStructs(typeinfoSymbols); + createTypeinfoStructs(); // process vtables and create classes for the vtables that have no typeinfo processVtables(); @@ -663,8 +663,6 @@ private boolean isValidVtableStart(Address vtableAddress) { return false; } - // TODO: maybe print a warning if the first item is not all zeros bc usually they are -- but pass - // it even then return true; } @@ -906,7 +904,7 @@ private int createVtableLongs(Address vtableAddress) { } } - private void createTypeinfoStructs(List typeinfoSymbols) throws CancelledException { + private void createTypeinfoStructs() throws CancelledException { StructureDataType classTypeInfoStructure = createClassTypeInfoStructure(); StructureDataType siClassTypeInfoStructure = @@ -914,23 +912,28 @@ private void createTypeinfoStructs(List typeinfoSymbols) throws Cancelle StructureDataType baseClassTypeInfoStructure = createBaseClassTypeInfoStructure(classTypeInfoStructure); - Iterator typeinfoIterator = typeinfoSymbols.iterator(); - while (typeinfoIterator.hasNext()) { - - monitor.checkCanceled(); - - Symbol typeinfoSymbol = typeinfoIterator.next(); - Address typeinfoAddress = typeinfoSymbol.getAddress(); - - // skip the typeinfo symbols from the three special typeinfos - if (isSpecialTypeinfo(typeinfoAddress)) { - continue; + List
typeinfoAddresses; + // get dwarf option + boolean isDwarfLoaded = isDwarfLoaded(); + // if dwarf get using symbols + if (isDwarfLoaded) { + typeinfoAddresses = getTypeinfoAddressesUsingSymbols(); + } + else { + // if not, get using ref to specials + if (hasExternalRelocationRefs()) { + typeinfoAddresses = getTypeinfoAddressesUsingRelocationTable(); } - // check for EXTERNAL block and look for specialTypeinfoRef there - // if fix works, put external block error message and to contact us - if (hasExternalBlock() && isSpecialVtable(typeinfoAddress)) { - continue; + else { + typeinfoAddresses = getTypeinfoAddressesUsingSpecialTypeinfos(); } + } + + if (typeinfoAddresses.isEmpty()) { + return; + } + + for (Address typeinfoAddress : typeinfoAddresses) { Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { @@ -955,52 +958,371 @@ private void createTypeinfoStructs(List typeinfoSymbols) throws Cancelle } } - try { - // create a "no inheritance" struct here - if (specialTypeinfoRef.equals(class_type_info) || - specialTypeinfoRef.equals(class_type_info_vtable)) { - api.clearListing(typeinfoAddress, - typeinfoAddress.add(classTypeInfoStructure.getLength())); - api.createData(typeinfoAddress, classTypeInfoStructure); - continue; - } + Data newStructure = null; - // create a "single inheritance" struct here - if (specialTypeinfoRef.equals(si_class_type_info) || - specialTypeinfoRef.equals(si_class_type_info_vtable)) { - api.clearListing(typeinfoAddress, - typeinfoAddress.add(siClassTypeInfoStructure.getLength() - 1)); - api.createData(typeinfoAddress, siClassTypeInfoStructure); - continue; - } + // create a "no inheritance" struct here + if (specialTypeinfoRef.equals(class_type_info) || + specialTypeinfoRef.equals(class_type_info_vtable)) { + + newStructure = applyTypeinfoStructure(classTypeInfoStructure, typeinfoAddress); + } - // create a "virtual multip inheritance" struct here - if (specialTypeinfoRef.equals(vmi_class_type_info) || - specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { - - // get num base classes - int offsetOfNumBases = 2 * defaultPointerSize + 4; - int numBases = api.getInt(typeinfoAddress.add(offsetOfNumBases)); - - // get or create the vmiClassTypeInfoStruct - Structure vmiClassTypeinfoStructure = - (Structure) dataTypeManager.getDataType(classDataTypesCategoryPath, - "VmiClassTypeInfoStructure" + numBases); - if (vmiClassTypeinfoStructure == null) { - vmiClassTypeinfoStructure = - createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); - } - api.clearListing(typeinfoAddress, - typeinfoAddress.add(vmiClassTypeinfoStructure.getLength() - 1)); - api.createData(typeinfoAddress, vmiClassTypeinfoStructure); + // create a "single inheritance" struct here + else if (specialTypeinfoRef.equals(si_class_type_info) || + specialTypeinfoRef.equals(si_class_type_info_vtable)) { + + newStructure = applyTypeinfoStructure(siClassTypeInfoStructure, typeinfoAddress); + } + // create a "virtual multip inheritance" struct here + else if (specialTypeinfoRef.equals(vmi_class_type_info) || + specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { + + Structure vmiClassTypeinfoStructure = + getOrCreateVmiTypeinfoStructure(typeinfoAddress, baseClassTypeInfoStructure); + if (vmiClassTypeinfoStructure != null) { + newStructure = applyTypeinfoStructure(vmiClassTypeinfoStructure, typeinfoAddress); } } - catch (Exception e) { - Msg.debug(this, "ERROR: Could not apply structure to " + typeinfoAddress); + + if (newStructure == null) { + Msg.debug(this, "ERROR: Could not apply typeinfo structure to " + typeinfoAddress); + } + + // check for existing symbol and if none, demangle the name and apply + Symbol typeinfoSymbol = api.getSymbolAt(typeinfoAddress); + if (typeinfoSymbol == null) { + typeinfoSymbol = createDemangledTypeinfoSymbol(typeinfoAddress); + if (typeinfoSymbol == null) { + Msg.debug(this, "Could not create demangled typeinfo symbol at " + + typeinfoAddress.toString()); + } + } + + if (typeinfoSymbol != null && typeinfoSymbol.getName().equals("typeinfo")) { + promoteToClassNamespace(typeinfoSymbol.getParentNamespace()); + continue; } + + + } + } + + private Data applyTypeinfoStructure(Structure typeInfoStructure, Address typeinfoAddress) + throws CancelledException, AddressOutOfBoundsException { + api.clearListing(typeinfoAddress, typeinfoAddress.add(typeInfoStructure.getLength() - 1)); + Data newStructure; + try { + newStructure = api.createData(typeinfoAddress, typeInfoStructure); + } + catch (Exception e) { + newStructure = null; + } + if (newStructure == null) { + Msg.debug(this, + "Could not create " + typeInfoStructure.getName() + " at " + typeinfoAddress); + } + return newStructure; + } + + private Structure getOrCreateVmiTypeinfoStructure(Address typeinfoAddress, + StructureDataType baseClassTypeInfoStructure) { + + // get num base classes + int offsetOfNumBases = 2 * defaultPointerSize + 4; + int numBases; + try { + numBases = api.getInt(typeinfoAddress.add(offsetOfNumBases)); + } + catch (MemoryAccessException | AddressOutOfBoundsException e) { + return null; + } + + // get or create the vmiClassTypeInfoStruct + Structure vmiClassTypeinfoStructure = + (Structure) dataTypeManager.getDataType(classDataTypesCategoryPath, + "VmiClassTypeInfoStructure" + numBases); + if (vmiClassTypeinfoStructure == null) { + vmiClassTypeinfoStructure = + createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); + } + return vmiClassTypeinfoStructure; + } + + private Symbol createDemangledTypeinfoSymbol(Address typeinfoAddress) { + + String mangledTypeinfo = getTypeinfoName(typeinfoAddress); + if (mangledTypeinfo == null) { + Msg.debug(this, "Could not get typeinfo string from " + typeinfoAddress.toString()); + return null; + } + + if (mangledTypeinfo.startsWith("*")) { + mangledTypeinfo = mangledTypeinfo.substring(1); + } + mangledTypeinfo = "_Z" + mangledTypeinfo; + + DemanglerOptions options = new DemanglerOptions(); + options.setDemangleOnlyKnownPatterns(false); + options.setApplySignature(false); + options.setDoDisassembly(false); + + DemangledObject demangled = DemanglerUtil.demangle(mangledTypeinfo); + if (demangled == null) { + Msg.debug(this, "Could not demangle typeinfo string at " + typeinfoAddress.toString()); + return null; + } + + String namespaceString = demangled.getNamespaceString(); + + Namespace classNamespace = createTypeinfoClassNamespace(namespaceString); + + Msg.debug(this, typeinfoAddress.toString() + " " + namespaceString); + + if (classNamespace == null) { + Msg.debug(this, + typeinfoAddress.toString() + + "Could not create a class namespace for demangled namespace string " + + namespaceString); + return null; + } + + // create the new typeinfo symbol in the demangled namespace + try { + Symbol newSymbol = symbolTable.createLabel(typeinfoAddress, "typeinfo", classNamespace, + SourceType.ANALYSIS); + return newSymbol; + } + catch (InvalidInputException e) { + Msg.error(this, + typeinfoAddress.toString() + " invalid input exception " + e.getMessage()); + return null; + } + catch (IllegalArgumentException e) { + Msg.debug(this, + typeinfoAddress.toString() + " illegal argument exception " + e.getMessage()); + return null; + } + + } + + private Namespace createTypeinfoClassNamespace(String namespaceString) { + + int indexOfColons = namespaceString.indexOf("::"); + Namespace namespace = globalNamespace; + while (indexOfColons != -1) { + String namespaceName = namespaceString.substring(0, indexOfColons); + Namespace newNamespace = getOrCreateNamespace(namespaceName, namespace); + if (newNamespace == null) { + return null; + } + namespace = newNamespace; + namespaceString = namespaceString.substring(indexOfColons + 2); + indexOfColons = namespaceString.indexOf("::"); + } + // the substring after the last :: is the class namespace we want to return + Namespace classNamespace = getOrCreateNamespace(namespaceString, namespace); + if (classNamespace == null) { + return null; + } + + if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { + classNamespace = promoteToClassNamespace(classNamespace); + } + + return classNamespace; + } + + private Namespace getOrCreateNamespace(String namespaceName, Namespace parentNamespace) { + + Namespace namespace = symbolTable.getNamespace(namespaceName, parentNamespace); + if (namespace == null) { + try { + namespace = symbolTable.createNameSpace(parentNamespace, namespaceName, + SourceType.ANALYSIS); + } + catch (DuplicateNameException e) { + // shouldn't happen since it only gets here if the symbol didn't exist in the first place + } + catch (InvalidInputException e) { + return null; + } + } + return namespace; + } + + private String getTypeinfoName(Address address) { + + Data dataAt = api.getDataAt(address); + if (dataAt == null) { + return null; + } + if (!(dataAt.getBaseDataType() instanceof Structure)) { + return null; + } + + Structure typeinfoStructure = (Structure) dataAt.getBaseDataType(); + if (!typeinfoStructure.getName().contains("ClassTypeInfoStructure")) { + return null; + } + DataTypeComponent typeinfoNameComponent = typeinfoStructure.getComponent(1); + DataType typeinfoNameDatatype = typeinfoNameComponent.getDataType(); + if (!(typeinfoNameDatatype instanceof Pointer)) { + return null; + } + + Address stringReference = + extraUtils.getSingleReferencedAddress(address.add(typeinfoNameComponent.getOffset())); + + Data stringData = api.getDataAt(stringReference); + if (stringData == null) { + return null; + } + int stringLen = stringData.getLength(); + MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), stringReference); + + StringDataType sdt = new StringDataType(); + + String str; + try { + str = (String) sdt.getValue(buf, sdt.getDefaultSettings(), stringLen); + } + catch (AddressOutOfBoundsException e) { + return null; + } + return str; + } + + /** + * Method to get a list typeinfo addresses using symbols + * @return a list of non-special typeinfo addresses that have "typeinfo" symbols + * @throws CancelledException if cancelled + */ + private List
getTypeinfoAddressesUsingSymbols() throws CancelledException { + + List
typeinfoAddresses = new ArrayList
(); + + List typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet( + program.getAddressFactory().getAddressSet(), "typeinfo", true); + + Iterator typeinfoIterator = typeinfoSymbols.iterator(); + while (typeinfoIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol typeinfoSymbol = typeinfoIterator.next(); + Address typeinfoAddress = typeinfoSymbol.getAddress(); + + // skip the typeinfo symbols from the three special typeinfos + if (isSpecialTypeinfo(typeinfoAddress)) { + continue; + } + // check for EXTERNAL block and look for specialTypeinfoRef there + // if fix works, put external block error message and to contact us + if (hasExternalBlock() && isSpecialVtable(typeinfoAddress)) { + continue; + } + + typeinfoAddresses.add(typeinfoAddress); + } + return typeinfoAddresses; + } + + /** + * Method to get a list typeinfo addresses using relocation table info + * @return a list of typeinfo addresses + * @throws CancelledException if cancelled + */ + private List
getTypeinfoAddressesUsingRelocationTable() throws CancelledException { + + List
typeinfoAddresses = new ArrayList
(); + + Iterator bookmarksIterator = + program.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR); + while (bookmarksIterator.hasNext()) { + monitor.checkCanceled(); + Bookmark bookmark = bookmarksIterator.next(); + if (bookmark.getCategory().equals("EXTERNAL Relocation") && + bookmarkContainsSpecialTypeinfoName(bookmark.getComment())) { + typeinfoAddresses.add(bookmark.getAddress()); + } + } + return typeinfoAddresses; + } + + private boolean bookmarkContainsSpecialTypeinfoName(String bookmarkComment) { + + if (bookmarkComment.contains("class_type_info")) { + return true; + } + + if (bookmarkComment.contains("si_class_type_info")) { + return true; + } + if (bookmarkComment.contains("vmi_class_type_info")) { + return true; + } + return false; + } + + /** + * Method to check to see if there are any EXTERNAL block relocations + * @return true if there are any EXTERNAL block relocations in the program, false otherwise + * @throws CancelledException if cancelled + */ + private boolean hasExternalRelocationRefs() throws CancelledException { + // if no external block then there won't be any refernces to special typeinfos in the external + // block so return empty list + if (!hasExternalBlock()) { + return false; + } + Iterator bookmarksIterator = + program.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR); + while (bookmarksIterator.hasNext()) { + monitor.checkCanceled(); + Bookmark bookmark = bookmarksIterator.next(); + if (bookmark.getCategory().equals("EXTERNAL Relocation")) { + return true; + } + } + return false; + } + + private List
getTypeinfoAddressesUsingSpecialTypeinfos() throws CancelledException { + + List
specialTypeinfoRefs = new ArrayList
(); + + Reference[] refsToClassTypeinfo = api.getReferencesTo(class_type_info); + for (Reference ref : refsToClassTypeinfo) { + monitor.checkCanceled(); + specialTypeinfoRefs.add(ref.getFromAddress()); + } + + Reference[] refsToSiClassTypeinfo = api.getReferencesTo(si_class_type_info); + for (Reference ref : refsToSiClassTypeinfo) { + monitor.checkCanceled(); + specialTypeinfoRefs.add(ref.getFromAddress()); + } + + Reference[] refsToVmiClassTypeinfo = api.getReferencesTo(vmi_class_type_info); + for (Reference ref : refsToVmiClassTypeinfo) { + monitor.checkCanceled(); + specialTypeinfoRefs.add(ref.getFromAddress()); + } + + return specialTypeinfoRefs; + } + + private boolean isDwarfLoaded() { + Options options = program.getOptions("Program Information"); + boolean isDwarfLoaded = false; + Object isPDBLoadedObject = options.getObject("DWARF Loaded", null); + if (isPDBLoadedObject != null) { + isDwarfLoaded = (boolean) isPDBLoadedObject; + } + return isDwarfLoaded; } private StructureDataType createClassTypeInfoStructure() { @@ -1136,7 +1458,6 @@ private List addGccClassParents(RecoveredClass recoveredClass, Msg.debug(this, "couldn't get int at address " + inheritanceFlagAddress.toString()); } - int baseClassArrayOffset = defaultPointerSize * 3; Data baseClassArrayData = typeinfoStructure.getComponentAt(baseClassArrayOffset); @@ -1155,9 +1476,8 @@ private List addGccClassParents(RecoveredClass recoveredClass, for (int i = 0; i < numParents; i++) { // get parent from pointer to parent typeinfo - Address parentRefAddress = - extraUtils.getAddress(typeinfoAddress, - baseClassArrayOffset + (i * 2 * defaultPointerSize)); + Address parentRefAddress = extraUtils.getAddress(typeinfoAddress, + baseClassArrayOffset + (i * 2 * defaultPointerSize)); if (parentRefAddress == null) { Msg.debug(this, "Could not access address " + typeinfoAddress.toString() + " plus offset " + baseClassArrayOffset); @@ -1461,7 +1781,7 @@ private List findVftablesFromVtables() throws Exception { // previously RecoveredClass classWithNoTypeinfoStruct = getClass(vtableNamespace); if (classWithNoTypeinfoStruct == null) { - addNoVftableClass(vtableNamespace); + createNewClass(vtableNamespace, false); continue; } @@ -1483,7 +1803,7 @@ private List findVftablesFromVtables() throws Exception { Address vftableAddress = extraUtils.getAddress(typeinfoAddress, defaultPointerSize); // no valid address here so continue if (vftableAddress == null) { - addNoVftableClass(vtableNamespace); + createNewClass(vtableNamespace, false); // if so should also add to no vftable class continue; } @@ -1513,8 +1833,7 @@ private boolean isSpecialTypeinfo(Address address) { } private boolean isSpecialVtable(Address address) { - if (address.equals(class_type_info_vtable) || - address.equals(si_class_type_info_vtable) || + if (address.equals(class_type_info_vtable) || address.equals(si_class_type_info_vtable) || address.equals(vmi_class_type_info_vtable)) { return true; } @@ -1546,15 +1865,13 @@ private List createClassesFromTypeinfoSymbols(List typei // TODO: make sure it is a valid typeinfo - Namespace classNamespace = typeinfoSymbol.getParentNamespace(); RecoveredClass recoveredClass = getClass(classNamespace); - // we don't know yet if this class has vftable so just add without for now if (recoveredClass == null) { - recoveredClass = addNoVftableClass(classNamespace); + recoveredClass = createNewClass(classNamespace, false); recoveredClasses.add(recoveredClass); classToTypeinfoMap.put(recoveredClass, typeinfoAddress); @@ -1592,7 +1909,6 @@ private List createClassesFromTypeinfoSymbols(List typei } - // per docs those on this list // have no bases (ie parents), and is also a base type for the other two class type // representations ie (si and vmi) @@ -2035,7 +2351,7 @@ private Structure createSimpleClassStructure(RecoveredClass recoveredClass, Structure classStructureDataType = new StructureDataType(classPath, className, structLen, dataTypeManager); - + List parentList = recoveredClass.getParentList(); // shouldn't happen but check anyway if (parentList.size() > 1) { @@ -2071,7 +2387,7 @@ else if (singleInheritedGccClasses.contains(recoveredClass)) { 0, baseClassStructure, baseClassStructure.getName(), monitor); } } - + // figure out class data, if any, create it and add to class structure int dataOffset = getDataOffset(recoveredClass, classStructureDataType); int dataLen = UNKNOWN; @@ -2113,4 +2429,3 @@ private boolean hasExternalBlock() { } } - diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index 8cef4518dd1..b50c82b7ccf 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -47,6 +47,7 @@ import ghidra.program.model.pcode.HighVariable; import ghidra.program.model.symbol.*; import ghidra.program.util.ProgramLocation; +import ghidra.util.Msg; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; @@ -937,10 +938,13 @@ private List recoverClassesFromClassHierarchyDescriptors( Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { -// println("RTTI_Class_Hierarchy_Descriptor at " + -// classHierarchyDescriptorAddress.toString() + -// " is not in a class namespace. Cannot process."); - continue; + classNamespace = promoteToClassNamespace(classNamespace); + if(classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { + Msg.debug(this, + classHierarchyDescriptorAddress.toString() + " Could not promote " + + classNamespace.getName(true) + " to a class namespace."); + continue; + } } List vftableSymbolsInNamespace = @@ -975,12 +979,12 @@ private List recoverClassesFromClassHierarchyDescriptors( List classesWithVftablesInNamespace = recoverClassesFromVftables(vftableSymbolsInNamespace, false, false); if (classesWithVftablesInNamespace.size() == 0) { - //println("No class recovered for namespace " + classNamespace.getName()); + Msg.debug(this,"No class recovered for namespace " + classNamespace.getName()); continue; } if (classesWithVftablesInNamespace.size() > 1) { -// println("Unexpected multiple classes recovered for namespace " + -// classNamespace.getName()); + Msg.debug(this,"Unexpected multiple classes recovered for namespace " + + classNamespace.getName()); continue; } @@ -1232,7 +1236,7 @@ private List getClassHierarchyFromRTTI(RecoveredClass recoveredC // if the namespace isn't in the map then it is a class // without a vftable and a new RecoveredClass object needs to be created if (getClass(pointedToNamespace) == null) { - addNoVftableClass(pointedToNamespace); + createNewClass(pointedToNamespace, false); } RecoveredClass pointedToClass = getClass(pointedToNamespace); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java index ed0b85d116c..bef192897a6 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java @@ -2672,26 +2672,29 @@ public boolean hasChildWithVBaseAndDestructor(RecoveredClass recoveredClass) return false; } + /** - * Method to add class with no vftable to the namespace map + * Method to create a new recovered class object and add it to the namespaceToClassMap * @param namespace the namespace to put the new class in - * @return the recovered class\ + * @param hasVftable true if class has at least one vftable, false otherwise + * @return the RecoveredClass object * @throws CancelledException if cancelled */ - public RecoveredClass addNoVftableClass(Namespace namespace) throws CancelledException { + public RecoveredClass createNewClass(Namespace namespace, boolean hasVftable) + throws CancelledException { String className = namespace.getName(); String classNameWithNamespace = namespace.getName(true); - CategoryPath classPath = - extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, - classNameWithNamespace); - RecoveredClass nonVftableClass = + CategoryPath classPath = extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, + classNameWithNamespace); + + RecoveredClass newClass = new RecoveredClass(className, classPath, namespace, dataTypeManager); - nonVftableClass.setHasVftable(false); + newClass.setHasVftable(hasVftable); - updateNamespaceToClassMap(namespace, nonVftableClass); - return nonVftableClass; + updateNamespaceToClassMap(namespace, newClass); + return newClass; } @@ -2735,24 +2738,7 @@ List recoverClassesFromVftables(List vftableSymbolList, } continue; } - - // promote any non-class namespaces in the vftableNamespace path to class namespaces - boolean success = promoteNamespaces(vftableNamespace.getSymbol()); - if (!success) { - if (DEBUG) { - Msg.debug(this, "Unable to promote all non-class namespaces for " + - vftableNamespace.getName(true)); - } - } - - String className = vftableNamespace.getName(); - String classNameWithNamespace = vftableNamespace.getName(true); - - // Create Data Type Manager Category for given class - CategoryPath classPath = - extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, - classNameWithNamespace); - + // get only the functions from the ones that are not already processed structures // return null if not an unprocessed table List virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol, @@ -2764,24 +2750,24 @@ List recoverClassesFromVftables(List vftableSymbolList, } // Check to see if already have an existing RecoveredClass object for the - // class associated with the current vftable. If so, it indicates multi-inheritance + // class associated with the current vftable. RecoveredClass recoveredClass = getClass(vftableNamespace); if (recoveredClass == null) { // Create a RecoveredClass object for the current class - recoveredClass = - new RecoveredClass(className, classPath, vftableNamespace, dataTypeManager); + recoveredClass = createNewClass(vftableNamespace, true); recoveredClass.addVftableAddress(vftableAddress); recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); - // add recovered class to map - updateNamespaceToClassMap(vftableNamespace, recoveredClass); // add it to the running list of RecoveredClass objects recoveredClasses.add(recoveredClass); } else { recoveredClass.addVftableAddress(vftableAddress); recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); + if (!recoveredClasses.contains(recoveredClass)) { + recoveredClasses.add(recoveredClass); + } } @@ -2795,8 +2781,6 @@ List recoverClassesFromVftables(List vftableSymbolList, Map vftableReferenceToFunctionMapping = createVftableReferenceToFunctionMapping(referencesToVftable); - // add this smaller mapping set to the global map - //vftableRefToFunctionMap.putAll(vftableReferenceToFunctionMapping); //vftableReferenceToFunctionMapping List possibleConstructorDestructorsForThisClass = @@ -2819,25 +2803,38 @@ List recoverClassesFromVftables(List vftableSymbolList, return recoveredClasses; } - private boolean promoteNamespaces(Symbol symbol) throws CancelledException { + public void promoteClassNamespaces(List recoveredClasses) + throws CancelledException { + + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = classIterator.next(); + Namespace classNamespace = recoveredClass.getClassNamespace(); + promoteNamespaces(classNamespace); + } + } + + private boolean promoteNamespaces(Namespace namespace) throws CancelledException { - Namespace namespace = symbol.getParentNamespace(); while (!namespace.isGlobal()) { monitor.checkCanceled(); SymbolType namespaceType = namespace.getSymbol().getSymbolType(); - if (namespaceType != SymbolType.CLASS) { - // if it is a namespace but not a class we need to promote it to a class namespace - if (namespaceType == SymbolType.NAMESPACE) { - namespace = promoteToClassNamespace(namespace); - if (namespace == null) { - return false; - } - if (DEBUG) { - Msg.debug(this, - "Promoted namespace " + namespace.getName() + " to a class namespace"); - } + // if it is a namespace but not a class and it is in our namespace map (which makes + // it a valid class) we need to promote it to a class namespace + if (namespaceType != SymbolType.CLASS && namespaceType == SymbolType.NAMESPACE && + namespaceToClassMap.get(namespace) != null) { + + namespace = promoteToClassNamespace(namespace); + if (namespace == null) { + return false; } + //if (DEBUG) { + Msg.debug(this, + "Promoted namespace " + namespace.getName(true) + " to a class namespace"); + //} } else { namespace = namespace.getParentNamespace(); @@ -2852,11 +2849,20 @@ private boolean promoteNamespaces(Symbol symbol) throws CancelledException { */ private Namespace promoteToClassNamespace(Namespace namespace) { + SymbolType symbolType = namespace.getSymbol().getSymbolType(); + if (symbolType == SymbolType.CLASS) { + return namespace; + } + + if (symbolType != SymbolType.NAMESPACE) { + return namespace; + } + try { Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace); - SymbolType symbolType = newClass.getSymbol().getSymbolType(); - if (symbolType == SymbolType.CLASS) { + SymbolType newSymbolType = newClass.getSymbol().getSymbolType(); + if (newSymbolType == SymbolType.CLASS) { return newClass; } if (DEBUG) { @@ -3246,7 +3252,8 @@ public boolean allAncestorDataHasBeenCreated(RecoveredClass recoveredClass) if (parentClasses.isEmpty()) { throw new Exception( - recoveredClass.getName() + " should not have an empty class hierarchy"); + recoveredClass.getClassNamespace().getName(true) + + " should not have an empty class hierarchy"); } // if size one it only includes self