ghidra/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java
ghidra007 80538d8c49 GP-1499 Recover classes script various improvements. Updated checks for whether program has DWARF and whether DWARF has been applied so it works for old and new cases.
Improved error handling. Updated deprecated graph methods which were no longer using defined colors. Various code cleanup.
2021-11-25 19:02:53 +00:00

2957 lines
95 KiB
Java

/* ###
* 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.
*/
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
package classrecovery;
import java.util.*;
import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.demangler.*;
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.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramMemoryUtil;
import ghidra.util.Msg;
import ghidra.util.bytesearch.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private static final String VMI_CLASS_TYPE_INFO_STRUCTURE = "VmiClassTypeInfoStructure";
private static final String BASE_CLASS_TYPE_INFO_STRUCTURE = "BaseClassTypeInfoStructure";
private static final String SI_CLASS_TYPE_INFO_STRUCTURE = "SiClassTypeInfoStructure";
private static final String CLASS_TYPE_INFO_STRUCTURE = "ClassTypeInfoStructure";
private static final String VTABLE_LABEL = "vtable";
private static final String CLASS_VTABLE_PTR_FIELD_EXT = "vftablePtr";
private static final int NONE = -1;
private static final int UNKNOWN = -2;
private static final boolean DEBUG = false;
Map<RecoveredClass, Address> classToTypeinfoMap = new HashMap<RecoveredClass, Address>();
Address class_type_info_vtable = null;
Address si_class_type_info_vtable = null;
Address vmi_class_type_info_vtable = null;
Address class_type_info = null;
Address si_class_type_info = null;
Address vmi_class_type_info = null;
List<RecoveredClass> nonInheritedGccClasses = new ArrayList<RecoveredClass>();
List<RecoveredClass> singleInheritedGccClasses = new ArrayList<RecoveredClass>();
List<RecoveredClass> multiAndOrVirtuallyInheritedGccClasses = new ArrayList<RecoveredClass>();
List<RecoveredClass> recoveredClasses = new ArrayList<RecoveredClass>();
private Map<RecoveredClass, Map<Integer, RecoveredClass>> classToParentOrderMap =
new HashMap<RecoveredClass, Map<Integer, RecoveredClass>>();
private Map<RecoveredClass, Map<RecoveredClass, Long>> classToParentOffsetMap =
new HashMap<RecoveredClass, Map<RecoveredClass, Long>>();
boolean isDwarfLoaded;
boolean replaceClassStructs;
public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool,
FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates,
boolean nameVfunctions, boolean isDwarfLoaded, boolean replaceExistingClassStructures,
TaskMonitor monitor) throws Exception {
super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions,
replaceExistingClassStructures, isDwarfLoaded, monitor);
this.isDwarfLoaded = isDwarfLoaded;
this.replaceClassStructs = replaceExistingClassStructures;
}
@Override
public boolean containsRTTI() throws CancelledException, InvalidInputException {
if (!hasSpecialVtable()) {
return false;
}
return true;
}
@Override
public boolean isValidProgramType() {
if (!isGcc()) {
return false;
}
return true;
}
@Override
public List<RecoveredClass> createRecoveredClasses() throws CancelledException, Exception {
processGccRTTI();
if (recoveredClasses == null) {
Msg.debug(this, "Could not recover gcc rtti classes");
return null;
}
createCalledFunctionMap(recoveredClasses);
createClassHierarchyListAndMapForGcc();
if (isDwarfLoaded) {
retrieveExistingClassStructures(recoveredClasses);
assignConstructorsAndDestructorsUsingExistingName(recoveredClasses);
}
else {
processConstructorAndDestructors();
}
createVftableOrderMap(recoveredClasses);
figureOutClassDataMembers(recoveredClasses);
createAndApplyClassStructures();
return recoveredClasses;
}
private boolean isGcc() {
boolean isELF = program.getExecutableFormat().contains("ELF");
if (!isELF) {
return false;
}
boolean isCompilerSpecGcc =
program.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase("gcc");
if (isCompilerSpecGcc) {
return true;
}
MemoryBlock commentBlock = program.getMemory().getBlock(".comment");
if (commentBlock == null) {
return false;
}
if (!commentBlock.isLoaded()) {
return false;
}
// check memory bytes in block for GCC: bytes
byte[] gccBytes = { (byte) 0x47, (byte) 0x43, (byte) 0x43, (byte) 0x3a };
byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
Address found = program.getMemory()
.findBytes(commentBlock.getStart(), commentBlock.getEnd(), gccBytes, maskBytes,
true, monitor);
if (found == null) {
return false;
}
return true;
}
/**
* Method to check for at least one special RTTI vtable
* @return true if the program has at least one special vtable, false if none
* @throws CancelledException if cancelled
* @throws InvalidInputException if bad characters creating labels
*/
private boolean hasSpecialVtable() throws CancelledException, InvalidInputException {
boolean hasSpecialVtable = createSpecialVtables();
return hasSpecialVtable;
}
private Address findSpecialVtable(String namespace, String name) throws CancelledException {
Address vtableAddress = null;
Symbol symbolInNamespaces = getSymbolInNamespaces(namespace, name, VTABLE_LABEL);
if (symbolInNamespaces != null) {
if (!symbolInNamespaces.isPrimary()) {
symbolInNamespaces.setPrimary();
}
vtableAddress = symbolInNamespaces.getAddress();
return vtableAddress;
}
// if there is just one address that has symbols containing both strings then it suggests
// mangled symbol since the above didn't find it
Address addressContainingBothStrings =
getSingleAddressOfSymbolContainingBothStrings(namespace, name);
if (addressContainingBothStrings == null) {
return null;
}
// try demangling all the symbols at this address
Symbol[] vtableSymbols = symbolTable.getSymbols(addressContainingBothStrings);
for (Symbol vtableSymbol : vtableSymbols) {
DemanglerCmd cmd =
new DemanglerCmd(addressContainingBothStrings, vtableSymbol.getName());
cmd.applyTo(program, monitor);
}
// now check again to see if we can find the namespace/name
symbolInNamespaces = getSymbolInNamespaces(namespace, name, VTABLE_LABEL);
if (symbolInNamespaces != null) {
if (!symbolInNamespaces.isPrimary()) {
symbolInNamespaces.setPrimary();
}
vtableAddress = symbolInNamespaces.getAddress();
return vtableAddress;
}
return null;
}
private void processGccRTTI() throws CancelledException, Exception {
// create the appropriate type of type info struct at the various typeinfo symbol locations
createTypeinfoStructs();
processVtables();
// process vtables and create classes for the vtables that have no typeinfo
List<Symbol> vftableSymbols = findVftablesFromVtables();
recoveredClasses = recoverClassesFromVftables(vftableSymbols, true, true);
// find all typeinfo symbols and get their class namespace and create RecoveredClass object
List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), "typeinfo", true);
// create class objects for each typeinfo struct and make a class to typeinfo mapping for each
createClassesFromTypeinfoSymbols(typeinfoSymbols);
updateClassesWithParentsAndFlags(typeinfoSymbols);
// update the vftable offset map
Iterator<RecoveredClass> recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
List<Address> vftableAddresses = recoveredClass.getVftableAddresses();
Iterator<Address> vftableAddressIterator = vftableAddresses.iterator();
while (vftableAddressIterator.hasNext()) {
monitor.checkCanceled();
Address vftableAddress = vftableAddressIterator.next();
Address offsetAddress = vftableAddress.subtract(2 * defaultPointerSize);
int offsetValue = (int) api.getLong(offsetAddress);
recoveredClass.addClassOffsetToVftableMapping(offsetValue, vftableAddress);
}
}
return;
}
private void updateClassesWithParentsAndFlags(List<Symbol> typeinfoSymbols) throws Exception {
// add properties and parents to each class
Iterator<Symbol> 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 (typeinfoAddress.equals(class_type_info) ||
typeinfoAddress.equals(si_class_type_info) ||
typeinfoAddress.equals(vmi_class_type_info)) {
continue;
}
Namespace classNamespace = typeinfoSymbol.getParentNamespace();
RecoveredClass recoveredClass = getClass(classNamespace);
if (recoveredClass == null) {
// this shoudln't be null at this point
if (DEBUG) {
Msg.debug(this,
"***Shouldn't be a null class here: " + classNamespace.getName());
}
recoveredClass = createNewClass(classNamespace, false);
recoveredClasses.add(recoveredClass);
}
else {
if (!recoveredClasses.contains(recoveredClass)) {
recoveredClasses.add(recoveredClass);
}
}
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) {
if (DEBUG) {
Msg.debug(this,
"No special typeinfo reference found. Cannot process typeinfo struct at " +
typeinfoAddress.toString());
}
continue;
}
if (!isSpecialTypeinfo(specialTypeinfoRef)) {
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (!hasExternalBlock()) {
if (DEBUG) {
Msg.debug(this,
"Special typeinfo reference is not equal to one of the three special type infos. Cannot process typeinfo struct at " +
typeinfoAddress.toString());
}
continue;
}
// use referenced vtable symbol name instead since when in EXTERNAL block
// since can't get at the typeinfo ref in that block
if (!isSpecialVtable(specialTypeinfoRef)) {
if (DEBUG) {
Msg.debug(this,
"Special typeinfo reference is not equal to one of the three special type infos. Cannot process typeinfo struct at " +
typeinfoAddress.toString());
}
continue;
}
}
if (specialTypeinfoRef.equals(class_type_info) ||
specialTypeinfoRef.equals(class_type_info_vtable)) {
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<Integer, RecoveredClass> orderToParentMap =
new HashMap<Integer, RecoveredClass>();
classToParentOrderMap.put(recoveredClass, orderToParentMap);
Map<RecoveredClass, Long> parentToOffsetMap = new HashMap<RecoveredClass, Long>();
classToParentOffsetMap.put(recoveredClass, parentToOffsetMap);
continue;
}
// per docs those on this list are
// classes containing only a single, public, non-virtual base at offset zero
if (specialTypeinfoRef.equals(si_class_type_info) ||
specialTypeinfoRef.equals(si_class_type_info_vtable)) {
RecoveredClass parentClass = getSiClassParent(typeinfoAddress);
if (parentClass == null) {
throw new Exception("Could not get si parent from typeinfoAddress " +
typeinfoAddress.toString());
}
if (DEBUG) {
Msg.debug(this,
recoveredClass.getName() + " adding si parent " + parentClass.getName());
}
updateClassWithParent(parentClass, recoveredClass);
recoveredClass.setHasSingleInheritance(true);
recoveredClass.setHasMultipleInheritance(false);
recoveredClass.setHasMultipleVirtualInheritance(false);
parentClass.setIsPublicClass(true);
recoveredClass.addParentToBaseTypeMapping(parentClass, false);
// add order to parent and parent offset
Map<Integer, RecoveredClass> orderToParentMap =
new HashMap<Integer, RecoveredClass>();
orderToParentMap.put(0, parentClass);
classToParentOrderMap.put(recoveredClass, orderToParentMap);
Map<RecoveredClass, Long> parentToOffsetMap = new HashMap<RecoveredClass, Long>();
parentToOffsetMap.put(parentClass, 0L);
classToParentOffsetMap.put(recoveredClass, parentToOffsetMap);
if (!recoveredClasses.contains(parentClass)) {
recoveredClasses.add(parentClass);
}
continue;
}
if (specialTypeinfoRef.equals(vmi_class_type_info) ||
specialTypeinfoRef.equals(vmi_class_type_info_vtable)) {
List<RecoveredClass> parents =
addGccClassParentsFromVmiStruct(recoveredClass, typeinfoAddress);
if (parents.isEmpty()) {
continue;
}
for (RecoveredClass parent : parents) {
monitor.checkCanceled();
if (!recoveredClasses.contains(parent)) {
recoveredClasses.add(parent);
}
}
}
}
return;
}
/**
* Method to process the primary vtable for each "vtable" label
* @throws Exception if Data cannot be created
*/
private void processVtables() throws Exception {
List<Symbol> listOfVtableSymbols = new ArrayList<Symbol>();
// if dwarf loaded then get vtables using symbols
if (!isDwarfLoaded) {
listOfVtableSymbols = findVtablesUsingTypeinfoRefs();
}
else {
listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false);
}
List<Symbol> copyListOfVtableSymbols = new ArrayList<Symbol>(listOfVtableSymbols);
Iterator<Symbol> vtableIterator = listOfVtableSymbols.iterator();
while (vtableIterator.hasNext()) {
monitor.checkCanceled();
Symbol vtableSymbol = vtableIterator.next();
Namespace vtableNamespace = vtableSymbol.getParentNamespace();
Address vtableAddress = vtableSymbol.getAddress();
processVtable(vtableAddress, vtableNamespace, true, copyListOfVtableSymbols);
}
return;
}
private List<Symbol> findVtablesUsingTypeinfoRefs() throws Exception {
List<Symbol> vtableSymbols = new ArrayList<Symbol>();
List<Address> typeinfoAddresses = getTypeinfoAddressesUsingSymbols();
if (typeinfoAddresses.isEmpty()) {
return vtableSymbols;
}
// find refs to typeinfo's that are not in functions, instructions, or typeinfo structs
// we only want ones that may be in vtables
List<Address> typeinfoReferencesNotInTypeinfoStructs =
findTypeinfoReferencesNotInTypeinfoStructs(typeinfoAddresses);
if (typeinfoReferencesNotInTypeinfoStructs.isEmpty()) {
return vtableSymbols;
}
for (Address typeinfoRef : typeinfoReferencesNotInTypeinfoStructs) {
monitor.checkCanceled();
Address typeinfoAddress = extraUtils.getPointer(typeinfoRef);
if (typeinfoAddress == null) {
continue;
}
Structure typeinfoStructure = getTypeinfoStructure(typeinfoAddress);
if (typeinfoStructure == null) {
continue;
}
if (!isValidClassInfoStructure(typeinfoStructure)) {
continue;
}
// get top of vtable
Address vtableAddress = getPrimaryVtableAddress(typeinfoRef);
if (vtableAddress == null) {
continue;
}
// create symbol
Symbol typeinfoSymbol = api.getSymbolAt(typeinfoAddress);
if (typeinfoSymbol == null) {
continue;
}
if (!typeinfoSymbol.getName().equals("typeinfo")) {
continue;
}
// check for construction table and make new namespace if so
Namespace classNamespace = typeinfoSymbol.getParentNamespace();
if (classNamespace.equals(globalNamespace)) {
throw new Exception("typeinfo has global namespace " + typeinfoAddress);
}
Symbol vtableSymbol = symbolTable.createLabel(vtableAddress, VTABLE_LABEL,
classNamespace, SourceType.ANALYSIS);
vtableSymbols.add(vtableSymbol);
api.setPlateComment(vtableAddress, "vtable for " + classNamespace.getName(true));
}
return vtableSymbols;
}
private Address getPrimaryVtableAddress(Address typeinfoRef) throws CancelledException {
// check the long just before and if not a zero then continue since the rest
// are internal vtables and will get processed when the main one does
Address longBeforeTypeinfoRef = getAddress(typeinfoRef, 0 - defaultPointerSize);
// if this address doesn't exist then continue since not a valid vtable
if (longBeforeTypeinfoRef == null) {
return null;
}
// check for appropriately sized long that is value 0 to make sure the
// vtable the typeinfo ref is in is the main one and skip otherwise since non-zero
// ones are internal vtables that will get processed with the main one
if (!extraUtils.hasNumZeros(longBeforeTypeinfoRef, defaultPointerSize)) {
return null;
}
Address vtableAddress = longBeforeTypeinfoRef;
MemoryBlock currentBlock = program.getMemory().getBlock(typeinfoRef);
// stop if top of mem block
// stop if bytes are an address
// 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 nextAddress = getAddress(vtableAddress, 0 - defaultPointerSize);
while (nextAddress != null &&
program.getMemory().getBlock(nextAddress).equals(currentBlock) &&
getPointerToDefinedMemory(nextAddress) == null) {
vtableAddress = nextAddress;
nextAddress = getAddress(vtableAddress, 0 - defaultPointerSize);
}
return vtableAddress;
}
private Address getPointerToDefinedMemory(Address address) {
Address pointer = extraUtils.getPointer(address);
if (pointer == null) {
return null;
}
if (program.getMemory().getAllInitializedAddressSet().contains(pointer)) {
return pointer;
}
return null;
}
private boolean isValidClassInfoStructure(Structure typeinfoStructure) {
String typeinfoStructureName = typeinfoStructure.getName();
if (typeinfoStructureName.equals(CLASS_TYPE_INFO_STRUCTURE)) {
return true;
}
if (typeinfoStructureName.equals(SI_CLASS_TYPE_INFO_STRUCTURE)) {
return true;
}
if (typeinfoStructureName.contains(VMI_CLASS_TYPE_INFO_STRUCTURE)) {
return true;
}
return false;
}
private Namespace createConstructionNamespace(Symbol vtableSymbol, Symbol vttSymbol)
throws Exception {
Namespace vtableNamespace = vtableSymbol.getParentNamespace();
Namespace inNamespace = vttSymbol.getParentNamespace();
String name = vtableNamespace.getName() + "-in-" + inNamespace.getName(true);
List<Namespace> namespacesByPath =
NamespaceUtils.getNamespaceByPath(program, vtableNamespace, name);
if (namespacesByPath.isEmpty()) {
Namespace newNamespace = NamespaceUtils.createNamespaceHierarchy(name, vtableNamespace,
program, SourceType.ANALYSIS);
return newNamespace;
}
if (namespacesByPath.size() == 1) {
return namespacesByPath.get(0);
}
throw new Exception(
"More than one namespace " + vtableNamespace.getName(true) + " " + name);
}
private Structure getTypeinfoStructure(Address typeinfoAddress) {
Data data = api.getDataAt(typeinfoAddress);
if (!isTypeinfoStruct(data)) {
return null;
}
return (Structure) data.getBaseDataType();
}
public List<Address> findTypeinfoReferencesNotInTypeinfoStructs(List<Address> typeinfoAddresses)
throws CancelledException {
MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Typeinfo References");
AddressSet searchSet = new AddressSet();
AddressSetView initializedSet = program.getMemory().getAllInitializedAddressSet();
AddressRangeIterator addressRanges = initializedSet.getAddressRanges();
while (addressRanges.hasNext()) {
monitor.checkCanceled();
AddressRange addressRange = addressRanges.next();
searchSet.add(addressRange.getMinAddress(), addressRange.getMaxAddress());
}
List<Address> validTypeinfoRefs = new ArrayList<Address>();
Iterator<Address> typeinfoIterator = typeinfoAddresses.iterator();
while (typeinfoIterator.hasNext()) {
monitor.checkCanceled();
Address typeinfoAddress = typeinfoIterator.next();
// check direct refs to see if they are in undefined area or not in function
byte[] bytes = ProgramMemoryUtil.getDirectAddressBytes(program, typeinfoAddress);
addByteSearchPattern(searcher, validTypeinfoRefs, typeinfoAddress, bytes, monitor);
}
searcher.search(program, searchSet, monitor);
return validTypeinfoRefs;
}
/**
* Method to add a search pattern, to the searcher, for the set of bytes representing a typeinfo
* address
* @param searcher the MemoryBytePatternSearcher
* @param typeinfoRefs a list typeinfo reference addresses that are not contained
* in a function, instruction, or a typeinfo structure
* @param typeinfoAddress the given typeinfo address
* @param bytes the bytes to search for
* @param taskMonitor a cancellable monitor
*/
private void addByteSearchPattern(MemoryBytePatternSearcher searcher,
List<Address> typeinfoRefs, Address typeinfoAddress, byte[] bytes,
TaskMonitor taskMonitor) {
// no pattern bytes.
if (bytes == null) {
return;
}
// Each time a match for this byte pattern ...
GenericMatchAction<Address> action = new GenericMatchAction<Address>(typeinfoAddress) {
@Override
public void apply(Program prog, Address addr, Match match) {
Function functionContainingTypeinfoRef =
prog.getListing().getFunctionContaining(addr);
Data dataContainingTypeinfoRef = prog.getListing().getDefinedDataContaining(addr);
Instruction instructionContainingAddr =
prog.getListing().getInstructionContaining(addr);
// check the direct references found with the searcher
// if not in function but is an instruction then create the function
// otherwise, add to the list to report to user
if (functionContainingTypeinfoRef == null && instructionContainingAddr == null &&
dataContainingTypeinfoRef == null) {
typeinfoRefs.add(addr);
}
else if (dataContainingTypeinfoRef != null &&
!isTypeinfoStruct(dataContainingTypeinfoRef)) {
typeinfoRefs.add(addr);
}
}
};
// create a Pattern of the bytes and the MatchAction to perform upon a match
GenericByteSequencePattern<Address> genericByteMatchPattern =
new GenericByteSequencePattern<>(bytes, action);
searcher.addPattern(genericByteMatchPattern);
}
/**
* Method to determine if the given data is a typeinfo structure
* @param data the given data
* @return true if the given data is a typeinfo structure, else return false
*/
private boolean isTypeinfoStruct(Data data) {
if (data == null) {
return false;
}
DataType baseDataType = data.getBaseDataType();
if (!(baseDataType instanceof Structure)) {
return false;
}
Structure structure = (Structure) baseDataType;
if (structure.getName().contains(CLASS_TYPE_INFO_STRUCTURE)) {
return true;
}
return false;
}
/**
* Method to create an appropriate type of vtable (primary, internal, or construction) and
* an associated VTT, if applicable
* @param vtableAddress the given vtable address
* @param vtableNamespace the namespace of the given vtable
* @param isPrimary true if the vtable is the primary one for the class
* @param listOfAllVtables list of all vtables
* @throws CancelledException if cancelled
*/
private void processVtable(Address vtableAddress, Namespace vtableNamespace, boolean isPrimary,
List<Symbol> listOfAllVtables) throws CancelledException, Exception {
// skip the special tables
if (vtableAddress.equals(class_type_info_vtable) ||
vtableAddress.equals(si_class_type_info_vtable) ||
vtableAddress.equals(vmi_class_type_info_vtable)) {
return;
}
Data dataAt = api.getDataAt(vtableAddress);
// first check to see it is an erroneous vtable that has been made a byte array
// if so, clear it and start looking for the typeinfo reference
if (dataAt != null && dataAt.isArray()) {
api.clearListing(vtableAddress);
}
if (dataAt != null && !dataAt.getDataType().getName().equals("long")) {
api.clearListing(vtableAddress);
}
// find the special type info ref
Address typeinfoAddress = findNextTypeinfoRef(vtableAddress);
if (typeinfoAddress == null) {
if (DEBUG) {
Msg.debug(this, vtableNamespace.getName() +
" vtable has no typeinfo ref after vtable at " + vtableAddress.toString());
}
return;
}
// create the typeinfo pointer if there isn't already one
Data typeinfoPtr = api.getDataAt(typeinfoAddress);
if (typeinfoPtr == null) {
DataType nullPointer = dataTypeManager.getPointer(null);
api.createData(typeinfoAddress, nullPointer);
}
// if not already named a construction-vtable then check to see if it is one so it can
// be renamed and the new namespace figured out
// know it isn't null because the of the vtable symbol iterator used to call this method in the first place
Symbol vtableSymbol = symbolTable.getPrimarySymbol(vtableAddress);
if (!vtableSymbol.getName().equals("construction-vtable") && listOfAllVtables != null) {
// get first VTT before this vtable
Symbol vttSymbolBeforeConstructionVtable = getVTTBefore(vtableSymbol.getAddress());
if (vttSymbolBeforeConstructionVtable != null) {
List<Address> subVTTs = getSubVTTs(vttSymbolBeforeConstructionVtable.getAddress());
if (!subVTTs.isEmpty()) {
int n = 0;
for (Address subVTTAddress : subVTTs) {
monitor.checkCanceled();
n++;
Symbol constructionVtableSymbol = getNthSymbolOnListAfterAddress(
vttSymbolBeforeConstructionVtable.getAddress(), listOfAllVtables, n);
if (constructionVtableSymbol.equals(vtableSymbol)) {
// change the namespace and name of the vtable
Namespace classNamespace = createConstructionNamespace(vtableSymbol,
vttSymbolBeforeConstructionVtable);
vtableSymbol.setNameAndNamespace("construction-vtable", classNamespace,
SourceType.ANALYSIS);
vtableNamespace = vtableSymbol.getParentNamespace();
// label the subVTTaddress
symbolTable.createLabel(subVTTAddress, "subVTT_" + n,
vttSymbolBeforeConstructionVtable.getParentNamespace(),
SourceType.ANALYSIS);
api.setPlateComment(vtableAddress,
"construction vtable " + n + " for class " +
vttSymbolBeforeConstructionVtable.getParentNamespace()
.getName(true));
}
}
}
}
}
// create longs from top of vtable to the typeinfoAddress
createLongs(vtableAddress, typeinfoAddress);
Address possibleVftableAddress = getAddress(typeinfoAddress, defaultPointerSize);
if (possibleVftableAddress == null) {
return;
}
int numFunctionPointers = getNumFunctionPointers(possibleVftableAddress, true, true);
if (numFunctionPointers == 0) {
// if not a vftable check for an internal vtable
boolean isInternalVtable =
createInternalVtable(possibleVftableAddress, vtableNamespace);
if (isInternalVtable) {
return;
}
// if not an internal vtable check for VTT table
boolean isVTT = createVTT(vtableNamespace, possibleVftableAddress);
if (isVTT) {
return;
}
return;
}
// if at least one function pointer make vftable label - the createVftable method will
// create the table late
String vftableLabel = VFTABLE_LABEL;
if (!isPrimary) {
vftableLabel = "internal_" + vftableLabel;
}
symbolTable.createLabel(possibleVftableAddress, vftableLabel, vtableNamespace,
SourceType.ANALYSIS);
createVftableArray(possibleVftableAddress, numFunctionPointers);
// check for an internal vtable after the vftable and make a symbol there if there is one
// will process them later
Address possibleInternalVtableAddress =
getAddress(possibleVftableAddress, defaultPointerSize * numFunctionPointers);
// if there is no symbol or a non-default symbol then the nextAddress is an internal
// vtable
if (possibleInternalVtableAddress == null) {
return;
}
// check to see if it is an internal vtable
boolean isInternalVtable =
createInternalVtable(possibleInternalVtableAddress, vtableNamespace);
if (isInternalVtable) {
return;
}
// otherwise check to see if it is a VTT table and create it if so
boolean isVTT = createVTT(vtableNamespace, possibleInternalVtableAddress);
if (isVTT) {
return;
}
}
private Symbol getVTTBefore(Address address) throws CancelledException {
// get all symbols named VTT and get the one directly before the given address
List<Symbol> vttSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), "VTT", true);
return getSymbolOnListBeforeAddress(address, vttSymbols);
}
private List<Address> getSubVTTs(Address vttAddress) {
// keep getting next code unit and continue while in the VTT (check for pointers)
// if there is a reference inside the vtt then count it - it is a subVTT
int offset = 0;
List<Address> subVtts = new ArrayList<Address>();
Address currentAddress = vttAddress;
while (currentAddress != null && getPointerToDefinedMemory(currentAddress) != null) {
if (offset > 0) {
Reference[] referencesTo = api.getReferencesTo(currentAddress);
if (referencesTo.length > 0) {
subVtts.add(currentAddress);
}
}
offset++;
currentAddress = getAddress(vttAddress, defaultPointerSize * offset);
}
return subVtts;
}
/*
* Method to get the address on list that is the first that comes after the given address
*/
private Symbol getSymbolOnListBeforeAddress(Address givenAddress, List<Symbol> listOfSymbols)
throws CancelledException {
if (listOfSymbols.isEmpty()) {
return null;
}
Symbol symbolBefore = null;
listOfSymbols.sort((a1, a2) -> a1.getAddress().compareTo(a2.getAddress()));
for (Symbol symbol : listOfSymbols) {
monitor.checkCanceled();
if (symbol.getAddress().getOffset() >= givenAddress.getOffset()) {
return symbolBefore;
}
if (symbolBefore == null) {
symbolBefore = symbol;
continue;
}
if (symbol.getAddress().getOffset() > symbolBefore.getAddress().getOffset()) {
symbolBefore = symbol;
}
}
return symbolBefore;
}
private Symbol getNthSymbolOnListAfterAddress(Address givenAddress, List<Symbol> listOfSymbols,
int n) throws CancelledException {
if (listOfSymbols.isEmpty()) {
return null;
}
int numSymbolsAfter = 0;
listOfSymbols.sort((a1, a2) -> a1.getAddress().compareTo(a2.getAddress()));
for (Symbol symbol : listOfSymbols) {
monitor.checkCanceled();
if (symbol.getAddress().getOffset() > givenAddress.getOffset()) {
numSymbolsAfter++;
if (numSymbolsAfter == n) {
return symbol;
}
}
}
return null;
}
private boolean createInternalVtable(Address possibleInternalVtableAddress,
Namespace vtableNamespace) throws CancelledException, InvalidInputException, Exception {
// check to see if it is a pointer and if so, it cannot be an internal vtable
// as they contain at least one long
Address pointer = getPointerToDefinedMemory(possibleInternalVtableAddress);
if (pointer != null) {
return false;
}
Symbol possibleInternalVtableSymbol =
symbolTable.getPrimarySymbol(possibleInternalVtableAddress);
if (possibleInternalVtableSymbol != null &&
possibleInternalVtableSymbol.getSource() != SourceType.DEFAULT &&
(!possibleInternalVtableSymbol.getParentNamespace().equals(vtableNamespace) ||
!possibleInternalVtableSymbol.getName().contains("vtable"))) {
return false;
}
if (possibleInternalVtableSymbol == null ||
(possibleInternalVtableSymbol.getSource() == SourceType.DEFAULT &&
(isValidVtableStart(possibleInternalVtableAddress) ||
isValidVftableStart(possibleInternalVtableAddress)))) {
symbolTable.createLabel(possibleInternalVtableAddress,
"internal_vtable_" + possibleInternalVtableAddress.toString(), vtableNamespace,
SourceType.ANALYSIS);
processVtable(possibleInternalVtableAddress, vtableNamespace, false, null);
return true;
}
return false;
}
/**
* Method to create a VTT table label at the given address if it is deemed a valid VTT
* @param classNamespace the given namespace
* @param address the address of the potential VTT table
* @return true if a valid VTT has been discovered and label created
* @throws Exception if data creation results in an exception
*/
private boolean createVTT(Namespace classNamespace, Address address) throws Exception {
// get pointer at address
Address pointer = getPointerToDefinedMemory(address);
if (pointer == null) {
return false;
}
// check to see if pointer is to the class vftable or to a class internal vtable or to itself
// if not one of those things it isn't a VTT
Symbol symbol = symbolTable.getPrimarySymbol(pointer);
if ((!symbol.getName().equals(VFTABLE_LABEL) ||
!symbol.getName().contains("internal_vtable")) &&
!symbol.getParentNamespace().equals(classNamespace) && !pointer.equals(address)) {
return false;
}
// if it is then create the VTT symbol and create pointer there
symbolTable.createLabel(address, "VTT", classNamespace, SourceType.ANALYSIS);
DataType nullPointer = dataTypeManager.getPointer(null);
try {
api.createData(pointer, nullPointer);
}
catch (Exception e) {
// already data there so don't try and overwrite it
}
api.setPlateComment(address, "VTT for " + classNamespace.getName(true));
return true;
}
private Data createVftableArray(Address vftableAddress, int numFunctionPointers)
throws Exception {
api.clearListing(vftableAddress,
vftableAddress.add((numFunctionPointers * defaultPointerSize - 1)));
DataType pointerDataType = dataTypeManager.getPointer(null);
ArrayDataType vftableArrayDataType =
new ArrayDataType(pointerDataType, numFunctionPointers, defaultPointerSize);
Data vftableArrayData = api.createData(vftableAddress, vftableArrayDataType);
return vftableArrayData;
}
/**
* Method to check for a valid vtable at the given address
* @param vtableAddress the given address
* @return true if there is a valid vtable at the given address, false otherwise
*/
private boolean isValidVtableStart(Address vtableAddress) {
// check that no refs into the first 2*defaultptr bytes
// skip top of table since that will have references to it
Address address = getAddress(vtableAddress, 1);
if (address == null) {
return false;
}
if (!areNoReferencesInto(address, 2 * defaultPointerSize - 1)) {
return false;
}
// check that no pointers
if (!areNoReferencesFrom(vtableAddress, 2 * defaultPointerSize)) {
return false;
}
// check that no other data exept possibly longs at correct offsets
if (!isNoDataCreatedExceptMaybeLongs(vtableAddress, 2 * defaultPointerSize)) {
return false;
}
return true;
}
private boolean areNoReferencesInto(Address topAddress, int length) {
int offset = 0;
MemoryBlock currentMemoryBlock = program.getMemory().getBlock(topAddress);
while (offset < length) {
Address address = getAddress(topAddress, offset);
if (address == null) {
return false;
}
if (!currentMemoryBlock.contains(address)) {
return false;
}
Reference[] referencesTo = extraUtils.getReferencesTo(address);
if (referencesTo.length > 0) {
return false;
}
offset++;
}
return true;
}
private boolean areNoReferencesFrom(Address topAddress, int length) {
int offset = 0;
MemoryBlock currentMemoryBlock = program.getMemory().getBlock(topAddress);
while (offset < length) {
Address address = getAddress(topAddress, offset);
if (address == null) {
return false;
}
if (!currentMemoryBlock.contains(address)) {
return false;
}
List<Address> referenceFromAddresses = extraUtils.getReferenceFromAddresses(address);
if (referenceFromAddresses.size() > 0) {
return false;
}
offset++;
}
return true;
}
private boolean isNoDataCreatedExceptMaybeLongs(Address startAddress, int length) {
int offset = 0;
MemoryBlock currentMemoryBlock = program.getMemory().getBlock(startAddress);
while (offset < length) {
Address address = getAddress(startAddress, offset);
if (address == null) {
return false;
}
if (!currentMemoryBlock.contains(address)) {
return false;
}
Data data = api.getDataAt(address);
// if there is data and it isn't on a pointer size boundary then return null
// if there is data and it is on a pointer size boundary but isn't a long then
// return null
// otherwise, continue
if (data != null) {
if (offset % defaultPointerSize == 0 &&
data.getBaseDataType().getName().equals("long")) {
offset += defaultPointerSize;
continue;
}
return false;
}
offset++;
}
return true;
}
private boolean isValidVftableStart(Address vftableAddress) throws CancelledException {
// no refs into first defaaultPointerSize bytes
Address address = getAddress(vftableAddress, 1);
if (address == null) {
return false;
}
if (!areNoReferencesInto(address, defaultPointerSize - 1)) {
return false;
}
if (extraUtils.hasNumZeros(vftableAddress, defaultPointerSize)) {
return true;
}
Data data = api.getDataAt(vftableAddress);
if (data != null) {
if (!data.isPointer()) {
return false;
}
Address referencedAddress = extraUtils.getSingleReferencedAddress(vftableAddress);
if (referencedAddress == null) {
return false;
}
Function functionAt = api.getFunctionAt(referencedAddress);
if (functionAt != null) {
return true;
}
}
else {
try {
Long longValue = api.getLong(address);
Address functionAddress = address.getNewAddress(longValue);
Function functionAt = api.getFunctionAt(functionAddress);
if (functionAt != null) {
return true;
}
}
catch (MemoryAccessException e) {
return false;
}
catch (AddressOutOfBoundsException e) {
return false;
}
}
return false;
}
/**
* Method to replace the array incorrectly placed at special vftable with longs followed by
* typeinfo label
* @param vtableAddress the given special vtable address
* @return the address of the typeinfo in the vtable if replace was successful, null otherwise
* @throws CancelledException if cancelled
* @throws InvalidInputException if bad characters when creating label
*/
private Address createSpecialVtable(Address vtableAddress)
throws CancelledException, InvalidInputException {
Symbol vtableSymbol = symbolTable.getPrimarySymbol(vtableAddress);
api.clearListing(vtableAddress);
int vtableLongs = createVtableLongs(vtableAddress);
if (vtableLongs > 0) {
Address typeinfoAddress = vtableAddress.add(vtableLongs * defaultPointerSize);
symbolTable.createLabel(typeinfoAddress, "typeinfo", vtableSymbol.getParentNamespace(),
SourceType.ANALYSIS);
return typeinfoAddress;
}
return null;
}
/**
* Method to create long data type at the given vtable address and return the number created OR
* if they are already created, just return how many there are
* @param vtableAddress the address of the given vtable
* @return the number of long data types at vtableAddress
*/
private int createVtableLongs(Address vtableAddress) {
AddressSetView programAddressSet = program.getMemory().getAllInitializedAddressSet();
DataType pointer = dataTypeManager.getPointer(null);
LongDataType longDT = new LongDataType();
int offset = 0;
int numLongs = 0;
while (true) {
Address address = vtableAddress.add(offset);
// Except for the first one which should have a symbol, if there is a symbol at the
// address, stop making longs because it there are no references into the vtable longs
if (offset > 0 && symbolTable.getPrimarySymbol(address) != null) {
return numLongs;
}
// create a pointer and check to see if it is a reference to a valid memory location
try {
api.createData(address, pointer);
Address referencedAddress = extraUtils.getSingleReferencedAddress(address);
// if it isn't a valid pointer, clear what we just created and increment to offset so
// the next can be checked
if (referencedAddress == null || !programAddressSet.contains(referencedAddress)) {
api.clearListing(address);
api.createData(address, longDT);
offset += defaultPointerSize;
numLongs++;
}
// if it is valid, leave the pointer created and get out of the loop
else {
return numLongs;
}
}
// if bump into existing data return the number found so far
catch (Exception e) {
return numLongs;
}
}
}
/**
* Method to create and apply typeinfo structs of one of the three types used by rtti classes
* @throws CancelledException if cancelled
* @throws Exception if could not apply a type info structure
*/
private void createTypeinfoStructs() throws CancelledException, Exception {
StructureDataType classTypeInfoStructure = createClassTypeInfoStructure();
StructureDataType siClassTypeInfoStructure =
createSiClassTypeInfoStructure(classTypeInfoStructure);
StructureDataType baseClassTypeInfoStructure =
createBaseClassTypeInfoStructure(classTypeInfoStructure);
List<Address> typeinfoAddresses;
// if dwarf get using symbols
if (isDwarfLoaded) {
typeinfoAddresses = getTypeinfoAddressesUsingSymbols();
}
else {
// if not, get using ref to specials
if (hasExternalRelocationRefs()) {
typeinfoAddresses = getTypeinfoAddressesUsingRelocationTable();
}
else {
typeinfoAddresses = getTypeinfoAddressesUsingSpecialTypeinfos();
}
}
if (typeinfoAddresses.isEmpty()) {
return;
}
for (Address typeinfoAddress : typeinfoAddresses) {
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) {
continue;
}
if (!isSpecialTypeinfo(specialTypeinfoRef)) {
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (!hasExternalBlock()) {
continue;
}
// use referenced vtable symbol name instead since when in EXTERNAL block
// since can't get at the typeinfo ref in that block
if (!isSpecialVtable(specialTypeinfoRef)) {
continue;
}
}
Data newStructure = null;
// create a "no inheritance" struct here
if (specialTypeinfoRef.equals(class_type_info) ||
specialTypeinfoRef.equals(class_type_info_vtable)) {
newStructure = applyTypeinfoStructure(classTypeInfoStructure, typeinfoAddress);
}
// 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);
}
}
if (newStructure == null) {
throw new Exception(
"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.getSource() == SourceType.DEFAULT) {
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, Exception {
api.clearListing(typeinfoAddress, typeinfoAddress.add(typeInfoStructure.getLength() - 1));
Data newStructure;
newStructure = api.createData(typeinfoAddress, typeInfoStructure);
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));
}
// if there isn't enough memory to get the int then return null
catch (MemoryAccessException | AddressOutOfBoundsException e) {
return null;
}
// get or create the vmiClassTypeInfoStruct
Structure vmiClassTypeinfoStructure = (Structure) dataTypeManager
.getDataType(classDataTypesCategoryPath, VMI_CLASS_TYPE_INFO_STRUCTURE + numBases);
if (vmiClassTypeinfoStructure == null) {
vmiClassTypeinfoStructure =
createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases);
}
return vmiClassTypeinfoStructure;
}
private Symbol createDemangledTypeinfoSymbol(Address typeinfoAddress)
throws DuplicateNameException, InvalidInputException {
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
Symbol newSymbol = symbolTable.createLabel(typeinfoAddress, "typeinfo", classNamespace,
SourceType.ANALYSIS);
return newSymbol;
}
private Namespace createTypeinfoClassNamespace(String namespaceString)
throws DuplicateNameException, InvalidInputException {
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)
throws DuplicateNameException, InvalidInputException {
Namespace namespace = symbolTable.getNamespace(namespaceName, parentNamespace);
if (namespace == null) {
namespace =
symbolTable.createNameSpace(parentNamespace, namespaceName, SourceType.ANALYSIS);
}
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(CLASS_TYPE_INFO_STRUCTURE)) {
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;
str = (String) sdt.getValue(buf, sdt.getDefaultSettings(), stringLen);
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<Address> getTypeinfoAddressesUsingSymbols() throws CancelledException {
List<Address> typeinfoAddresses = new ArrayList<Address>();
List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), "typeinfo", true);
Iterator<Symbol> 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<Address> getTypeinfoAddressesUsingRelocationTable() throws CancelledException {
List<Address> typeinfoAddresses = new ArrayList<Address>();
Iterator<Bookmark> 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<Bookmark> 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;
}
/**
* Get the references to the special type infos that exist in the current program.
* @return the references to the special type infos that exist in the current program
* @throws CancelledException if cancelled
*/
private List<Address> getTypeinfoAddressesUsingSpecialTypeinfos() throws CancelledException {
List<Address> specialTypeinfoRefs = new ArrayList<Address>();
if (class_type_info != null) {
Reference[] refsToClassTypeinfo = api.getReferencesTo(class_type_info);
for (Reference ref : refsToClassTypeinfo) {
monitor.checkCanceled();
specialTypeinfoRefs.add(ref.getFromAddress());
}
}
if (si_class_type_info != null) {
Reference[] refsToSiClassTypeinfo = api.getReferencesTo(si_class_type_info);
for (Reference ref : refsToSiClassTypeinfo) {
monitor.checkCanceled();
specialTypeinfoRefs.add(ref.getFromAddress());
}
}
if (vmi_class_type_info != null) {
Reference[] refsToVmiClassTypeinfo = api.getReferencesTo(vmi_class_type_info);
for (Reference ref : refsToVmiClassTypeinfo) {
monitor.checkCanceled();
specialTypeinfoRefs.add(ref.getFromAddress());
}
}
return specialTypeinfoRefs;
}
/**
* Method to call the various methods to determine whether the functions that make references to
* the vftables are constructors, destructors, deleting destructors, clones, or vbase functions
* @throws CancelledException if cancelled
* @throws InvalidInputException if issues setting function return
* @throws DuplicateNameException if try to create same symbol name already in namespace
* @Exception if issues making labels
*/
private void processConstructorAndDestructors()
throws CancelledException, InvalidInputException, DuplicateNameException, Exception {
// find deleting destructors using various mechanisms
// findDeletingDestructors(recoveredClasses);
// use atexit param list to find more destructors
// findDestructorsUsingAtexitCalledFunctions(recoveredClasses);
// figure out which are inlined and put on separate list to be processed later
separateInlinedConstructorDestructors(recoveredClasses);
// figure out which member functions are constructors and which are destructors
// using the order their parents are called
processRegularConstructorsAndDestructorsUsingCallOrder(recoveredClasses);
// determine which of the inlines are constructors and which are destructors
processInlinedConstructorsAndDestructors(recoveredClasses);
findConstructorsAndDestructorsUsingAncestorClassFunctions(recoveredClasses);
findInlineConstructorsAndDestructorsUsingRelatedClassFunctions(recoveredClasses);
// use the load/store information from decompiler to figure out as many of the
// ones that could not be determined in earlier stages
processRemainingIndeterminateConstructorsAndDestructors(recoveredClasses);
// use the known constructors and known vfunctions to figure out
// clone functions
// findCloneFunctions(recoveredClasses);
// This has to be here. It needs all the info from the previously run methods to do this.
// Finds the constructors that have multiple basic blocks, reference the vftable not in the
// first block, and call non-parent constructors and non operator new before the vftable ref
// findMoreInlinedConstructors(recoveredClasses);
// findDestructorsWithNoParamsOrReturn(recoveredClasses);
// use vftables with references to all the same function (except possibly one deleting
// destructor)to find the purecall function
// identifyPureVirtualFunction(recoveredClasses);
// findRealVBaseFunctions(recoveredClasses);
}
private StructureDataType createClassTypeInfoStructure() {
StructureDataType classTypeInfoStructure = new StructureDataType(classDataTypesCategoryPath,
CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager);
CharDataType characterDT = new CharDataType();
DataType pointer = dataTypeManager.getPointer(null);
DataType charPointer = dataTypeManager.getPointer(characterDT);
classTypeInfoStructure.add(pointer, "classTypeinfoPtr", null);
classTypeInfoStructure.add(charPointer, "typeinfoName", null);
classTypeInfoStructure.setPackingEnabled(true);
return classTypeInfoStructure;
}
private StructureDataType createSiClassTypeInfoStructure(
StructureDataType classTypeInfoStructure) {
StructureDataType siClassTypeInfoStructure = new StructureDataType(
classDataTypesCategoryPath, SI_CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager);
CharDataType characterDT = new CharDataType();
DataType pointer = dataTypeManager.getPointer(null);
DataType charPointer = dataTypeManager.getPointer(characterDT);
siClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null);
siClassTypeInfoStructure.add(charPointer, "typeinfoName", null);
DataType pointerToClassTypeInfoStruct = dataTypeManager.getPointer(classTypeInfoStructure);
siClassTypeInfoStructure.add(pointerToClassTypeInfoStruct, "baseClassTypeInfoPtr", null);
siClassTypeInfoStructure.setPackingEnabled(true);
return siClassTypeInfoStructure;
}
private StructureDataType createBaseClassTypeInfoStructure(
StructureDataType classTypeInfoStructure) throws InvalidDataTypeException {
StructureDataType baseclassTypeInfoStructure = new StructureDataType(
classDataTypesCategoryPath, BASE_CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager);
DataType classTypeInfoPointer = dataTypeManager.getPointer(classTypeInfoStructure);
int offsetBitSize = 24;
if (defaultPointerSize == 8) {
offsetBitSize = 56;
}
baseclassTypeInfoStructure.add(classTypeInfoPointer, "classTypeinfoPtr", null);
if (program.getMemory().isBigEndian()) {
baseclassTypeInfoStructure.addBitField(LongDataType.dataType, offsetBitSize,
"baseClassOffset", null);
baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isPublicBase",
null);
baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isVirtualBase",
null);
baseclassTypeInfoStructure.addBitField(ByteDataType.dataType, 6, "unused", null);
}
else {
baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isVirtualBase",
null);
baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isPublicBase",
null);
baseclassTypeInfoStructure.addBitField(ByteDataType.dataType, 6, "unused", null);
baseclassTypeInfoStructure.addBitField(LongDataType.dataType, offsetBitSize,
"baseClassOffset", null);
}
baseclassTypeInfoStructure.setPackingEnabled(true);
return baseclassTypeInfoStructure;
}
private StructureDataType createVmiClassTypeInfoStructure(
StructureDataType baseClassTypeInfoStructure, int numBaseClasses) {
StructureDataType vmiClassTypeInfoStructure =
new StructureDataType(classDataTypesCategoryPath,
VMI_CLASS_TYPE_INFO_STRUCTURE + numBaseClasses, 0, dataTypeManager);
CharDataType characterDT = new CharDataType();
UnsignedIntegerDataType unsignedIntDT = new UnsignedIntegerDataType();
DataType pointer = dataTypeManager.getPointer(null);
DataType charPointer = dataTypeManager.getPointer(characterDT);
vmiClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null);
vmiClassTypeInfoStructure.add(charPointer, "typeinfoName", null);
vmiClassTypeInfoStructure.add(unsignedIntDT, "flags", null);
vmiClassTypeInfoStructure.add(unsignedIntDT, "numBaseClasses", null);
// make array of base class type info structs
ArrayDataType baseClassArray = new ArrayDataType(baseClassTypeInfoStructure, numBaseClasses,
baseClassTypeInfoStructure.getLength());
vmiClassTypeInfoStructure.add(baseClassArray, "baseClassPtrArray", null);
vmiClassTypeInfoStructure.setPackingEnabled(true);
return vmiClassTypeInfoStructure;
}
/**
* Method to add parents to the given gcc class
* @param recoveredClass the given class
* @param typeinfoAddress the address of the typeinfo
* @return list of parents for the given class
* @throws Exception if cannot access the given typeinfo structure, one of its components, or it is not a vmi structure
*/
private List<RecoveredClass> addGccClassParentsFromVmiStruct(RecoveredClass recoveredClass,
Address typeinfoAddress) throws Exception {
Structure vmiTypeinfoStructure = getTypeinfoStructure(typeinfoAddress);
if (vmiTypeinfoStructure == null ||
!vmiTypeinfoStructure.getName().contains(VMI_CLASS_TYPE_INFO_STRUCTURE)) {
throw new Exception(
"Could not get vmi base typeinfo structure at address " + typeinfoAddress);
}
// process the inheritance flag
DataTypeComponent inheritanceFlagComponent = vmiTypeinfoStructure.getComponent(2);
int flagOffset = inheritanceFlagComponent.getOffset();
DataType inheritanceFlagDataType = inheritanceFlagComponent.getDataType();
MemBuffer buf =
new DumbMemBufferImpl(program.getMemory(), getAddress(typeinfoAddress, flagOffset));
Scalar scalar = (Scalar) inheritanceFlagDataType.getValue(buf,
inheritanceFlagDataType.getDefaultSettings(), inheritanceFlagDataType.getLength());
long inheritanceFlagValue = scalar.getUnsignedValue();
// 0x01: class has non-diamond repeated inheritance
// 0x02: class is diamond shaped
// add flag for non-diamond repeated and diamond shape types
if (inheritanceFlagValue == 1) {
if (DEBUG) {
Msg.debug(this,
"from typeinfo at address " + typeinfoAddress.toString() + " " +
recoveredClass.getClassNamespace().getName(true) +
" has non-diamond repeated inheritance");
}
}
if (inheritanceFlagValue == 2) {
recoveredClass.setIsDiamondShaped(true);
}
// process the base classes
// create parent maps
Map<Integer, RecoveredClass> orderToParentMap = new HashMap<Integer, RecoveredClass>();
Map<RecoveredClass, Long> parentToOffsetMap = new HashMap<RecoveredClass, Long>();
DataTypeComponent numBaseClassesComponent = vmiTypeinfoStructure.getComponent(3);
int numBaseClassesOffset = numBaseClassesComponent.getOffset();
DataType numBaseClassesDataType = numBaseClassesComponent.getDataType();
buf = new DumbMemBufferImpl(program.getMemory(),
getAddress(typeinfoAddress, numBaseClassesOffset));
scalar = (Scalar) numBaseClassesDataType.getValue(buf,
numBaseClassesDataType.getDefaultSettings(), numBaseClassesDataType.getLength());
int numBaseClasses = (int) scalar.getUnsignedValue();
if (numBaseClasses > 1) {
recoveredClass.setHasMultipleInheritance(true);
recoveredClass.setHasSingleInheritance(false);
}
else {
recoveredClass.setHasMultipleInheritance(false);
recoveredClass.setHasSingleInheritance(true);
}
// process the base class array
DataTypeComponent baseClassArrayComponent = vmiTypeinfoStructure.getComponent(4);
if (baseClassArrayComponent == null) {
throw new Exception(
"Could not get base class array in vmi structure at " + typeinfoAddress.toString());
}
int baseClassArrayOffset = baseClassArrayComponent.getOffset();
List<RecoveredClass> parentClassList = new ArrayList<RecoveredClass>();
int numParents = numBaseClasses;
for (int i = 0; i < numParents; i++) {
// get parent from pointer to parent typeinfo
Address parentRefAddress =
getAddress(typeinfoAddress, baseClassArrayOffset + (i * 2 * defaultPointerSize));
RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentRefAddress);
if (parentClass == null) {
throw new Exception("Could not get parent class number " + (i + 1) +
" from typeinfo struct at " + typeinfoAddress.toString());
}
if (DEBUG) {
Msg.debug(this,
recoveredClass.getName() + " adding vmi parent " + parentClass.getName());
}
updateClassWithParent(parentClass, recoveredClass);
parentClassList.add(parentClass);
LongDataType longDT = new LongDataType();
// get public/virtual/offset flag
Address flagAddress = getAddress(typeinfoAddress,
baseClassArrayOffset + (i * 2 * defaultPointerSize + defaultPointerSize));
buf = new DumbMemBufferImpl(program.getMemory(), flagAddress);
Scalar value =
(Scalar) longDT.getValue(buf, longDT.getDefaultSettings(), defaultPointerSize);
long publicVirtualOffsetFlag = value.getSignedValue();
//The low-order byte of __offset_flags contains flags, as given by the masks
//from the enumeration __offset_flags_masks:
//0x1: Base class is virtual
//0x2: Base class is public
boolean isVirtual = false;
boolean isPublic = false;
long virtualMask = 0x1L;
long publicMask = 0x2L;
long offsetMask;
if (defaultPointerSize == 4) {
offsetMask = 0xffffff00L;
}
else {
offsetMask = 0xffffffffffffff00L;
}
if ((publicVirtualOffsetFlag & virtualMask) == 1) {
isVirtual = true;
}
if (recoveredClass.hasMultipleInheritance()) {
recoveredClass.setHasMultipleVirtualInheritance(isVirtual);
}
recoveredClass.addParentToBaseTypeMapping(parentClass, isVirtual);
recoveredClass.setInheritsVirtualAncestor(isVirtual);
if (((publicVirtualOffsetFlag & publicMask) >> 1) == 1) {
isPublic = true;
}
parentClass.setIsPublicClass(isPublic);
// from doc:
//All but the lower 8 bits of __offset_flags are a signed offset. For a
//non-virtual base, this is the offset in the object of the base subobject.
//For a virtual base, this is the offset in the virtual table of the
//virtual base offset for the virtual base referenced (negative).
long offset = (publicVirtualOffsetFlag & offsetMask) >> 8;
if (DEBUG) {
Msg.debug(this, "typeinfo " + typeinfoAddress + " base [" + i + "] isVirtual = " +
isVirtual + " isPublic = " + isPublic + " offset = " + offset);
}
// add order to parent and parent offset
orderToParentMap.put(i, parentClass);
parentToOffsetMap.put(parentClass, offset);
continue;
}
if (DEBUG) {
Msg.debug(this, recoveredClass.getName() + " has " + numParents + " parents");
}
classToParentOrderMap.put(recoveredClass, orderToParentMap);
classToParentOffsetMap.put(recoveredClass, parentToOffsetMap);
return parentClassList;
}
/**
* Get the parent class given the typeinfo address of an Si class
* @param typeinfoAddress the given Si class's typeinfo Address
* @return the parent class
* @throws Exception if cannot access parent's type info reference address or if could not get
* the parent class
*/
private RecoveredClass getSiClassParent(Address typeinfoAddress) throws Exception {
int offset = defaultPointerSize * 2;
Address parentTypeinfoRef = getAddress(typeinfoAddress, offset);
if (parentTypeinfoRef == null) {
throw new Exception("Could not access address " + typeinfoAddress.toString() +
" plus offset " + offset);
}
RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentTypeinfoRef);
return parentClass;
}
/**
* Method to return the parent class given a reference to the parent class's typeinfo struct
* @param parentTypeinfoRef the given parent typeinfo reference
* @return the associated parent class
*/
private RecoveredClass getParentClassFromParentTypeInfoRef(Address parentTypeinfoRef) {
Address parentAddress = extraUtils.getSingleReferencedAddress(parentTypeinfoRef);
if (parentAddress == null) {
return null;
}
Symbol parentSymbol = symbolTable.getPrimarySymbol(parentAddress);
if (parentSymbol == null) {
return null;
}
Namespace parentNamespace = parentSymbol.getParentNamespace();
if (parentNamespace == null) {
return null;
}
RecoveredClass parentClass = getClass(parentNamespace);
if (parentClass == null) {
return null;
}
return parentClass;
}
/**
* Method to find the (up to three) special gcc vtables and replace the incorrectly made array with the
* correct data types. Also creates a type info symbol at the correct offset in the table.
* @return true if all found tables have a typeinfo symbol created successfully
* @throws CancelledException if cancelled
* @throws InvalidInputException if bad characters creating labels
*/
private boolean createSpecialVtables() throws CancelledException, InvalidInputException {
class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info");
class_type_info = null;
if (class_type_info_vtable == null) {
Msg.debug(this, "__class_type_info vtable not found --> no classes without parents");
}
else {
class_type_info = createSpecialVtable(class_type_info_vtable);
if (class_type_info == null) {
Msg.debug(this,
"__class_type_info typeinfo not found -- cannot continue gcc rtti processing");
return false;
}
}
si_class_type_info = null;
si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info");
if (si_class_type_info_vtable == null) {
Msg.debug(this, "__si_class_type_info vtable not found --> no single parent classes");
}
else {
si_class_type_info = createSpecialVtable(si_class_type_info_vtable);
if (si_class_type_info == null) {
Msg.debug(this,
"__si_class_type_info typeinfo not found -- cannot continue gcc rtti processing");
return false;
}
}
vmi_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__vmi_class_type_info");
vmi_class_type_info = null;
if (vmi_class_type_info_vtable == null) {
Msg.debug(this, "__vmi_class_type_info vtable not found --> no multi-parent classes");
}
else {
vmi_class_type_info = createSpecialVtable(vmi_class_type_info_vtable);
if (vmi_class_type_info == null) {
Msg.debug(this,
"__vmi_class_type_info typeinfo not found -- cannot continue gcc rtti processing");
return false;
}
}
if (class_type_info_vtable == null && si_class_type_info_vtable == null &&
vmi_class_type_info_vtable == null) {
Msg.debug(this,
"Since there are no class typeinfo tables this program does not appear to have RTTI.");
return false;
}
return true;
}
/**
* Method to find the next reference to a typeinfo symbol after the given address
* @param startAddress the address to start looking from
* @return the address of the next typeinfo address after the given address
*/
private Address findNextTypeinfoRef(Address startAddress) {
int offset = 0;
Address address = extraUtils.getAddress(startAddress, offset);
MemoryBlock currentMemoryBlock = program.getMemory().getBlock(startAddress);
while (address != null && currentMemoryBlock.contains(address)) {
Symbol symbol = symbolTable.getPrimarySymbol(address);
// if the symbol we find is not a default symbol
// because we have reached the end of the item we are searching
if (!address.equals(startAddress) && symbol != null &&
symbol.getSource() != SourceType.DEFAULT) {
return null;
}
Address possibleTypeinfo = extraUtils.getPointer(address);
if (possibleTypeinfo == null) {
offset += defaultPointerSize;
address = extraUtils.getAddress(startAddress, offset);
continue;
}
Symbol possibleTypeinfoSymbol = symbolTable.getPrimarySymbol(possibleTypeinfo);
if (possibleTypeinfoSymbol != null &&
possibleTypeinfoSymbol.getName().equals("typeinfo")) {
return address;
}
offset += defaultPointerSize;
address = extraUtils.getAddress(startAddress, offset);
}
return null;
}
/**
* Method to process the primary vtable for each "vtable" label
* @return the vftable Address in the vtable
* @throws Exception if Data cannot be created
*/
private List<Symbol> findVftablesFromVtables() throws Exception {
List<Symbol> vftableSymbols = new ArrayList<Symbol>();
// find all vtable symbols
List<Symbol> listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), VTABLE_LABEL, true);
Iterator<Symbol> vtableIterator = listOfVtableSymbols.iterator();
while (vtableIterator.hasNext()) {
monitor.checkCanceled();
Symbol vtableSymbol = vtableIterator.next();
Namespace vtableNamespace = vtableSymbol.getParentNamespace();
Address vtableAddress = vtableSymbol.getAddress();
// skip the special tables
if (vtableAddress.equals(class_type_info_vtable) ||
vtableAddress.equals(si_class_type_info_vtable) ||
vtableAddress.equals(vmi_class_type_info_vtable)) {
continue;
}
Data vtableData = api.getDataAt(vtableAddress);
if (vtableData == null) {
continue;
}
// find the special type info ref
Address typeinfoAddress = findNextTypeinfoRef(vtableAddress);
if (typeinfoAddress == null) {
if (DEBUG) {
Msg.debug(this, vtableAddress.toString() + " " + vtableNamespace.getName() +
" vtable has no typeinfo ref");
}
continue;
}
Address vftableAddress = extraUtils.getAddress(typeinfoAddress, defaultPointerSize);
// no valid address here so continue
if (vftableAddress == null) {
//createNewClass(vtableNamespace, false);
// if so should also add to no vftable class
continue;
}
Symbol vftableSymbol = symbolTable.getPrimarySymbol(vftableAddress);
if (vftableSymbol == null) {
continue;
}
if (vftableSymbol.getName().equals(VFTABLE_LABEL)) {
vftableSymbols.add(vftableSymbol);
}
}
return vftableSymbols;
}
/**
* Method to check if given typeinfo is one of the three special ones
* @param address the given typeinfo address
* @return true if it is a special one, false otherwise
*/
private boolean isSpecialTypeinfo(Address address) {
if (address.equals(class_type_info) || address.equals(si_class_type_info) ||
address.equals(vmi_class_type_info)) {
return true;
}
return false;
}
private boolean isSpecialVtable(Address address) {
if (address.equals(class_type_info_vtable) || address.equals(si_class_type_info_vtable) ||
address.equals(vmi_class_type_info_vtable)) {
return true;
}
return false;
}
private void createClassesFromTypeinfoSymbols(List<Symbol> typeinfoSymbols)
throws CancelledException {
Iterator<Symbol> 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;
}
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 = createNewClass(classNamespace, false);
recoveredClasses.add(recoveredClass);
classToTypeinfoMap.put(recoveredClass, typeinfoAddress);
}
if (recoveredClass != null && !classToTypeinfoMap.containsKey(recoveredClass)) {
classToTypeinfoMap.put(recoveredClass, typeinfoAddress);
}
if (!recoveredClasses.contains(recoveredClass)) {
recoveredClasses.add(recoveredClass);
}
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) {
if (DEBUG) {
Msg.debug(this,
"No special typeinfo reference found. Cannot process typeinfo struct at " +
typeinfoAddress.toString());
}
continue;
}
if (!isSpecialTypeinfo(specialTypeinfoRef)) {
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (!hasExternalBlock()) {
continue;
}
// use referenced vtable symbol name instead since when in EXTERNAL block
// since can't get at the typeinfo ref in that block
if (!isSpecialVtable(specialTypeinfoRef)) {
continue;
}
}
// 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)
if (specialTypeinfoRef.equals(class_type_info) ||
specialTypeinfoRef.equals(class_type_info_vtable)) {
nonInheritedGccClasses.add(recoveredClass);
recoveredClass.setHasSingleInheritance(true);
recoveredClass.setHasParentClass(false);
recoveredClass.setInheritsVirtualAncestor(false);
continue;
}
// per docs those on this list are
// classes containing only a single, public, non-virtual base at offset zero
if (specialTypeinfoRef.equals(si_class_type_info) ||
specialTypeinfoRef.equals(si_class_type_info_vtable)) {
singleInheritedGccClasses.add(recoveredClass);
recoveredClass.setHasSingleInheritance(true);
recoveredClass.setInheritsVirtualAncestor(false);
continue;
}
if (specialTypeinfoRef.equals(vmi_class_type_info) ||
specialTypeinfoRef.equals(vmi_class_type_info_vtable)) {
multiAndOrVirtuallyInheritedGccClasses.add(recoveredClass);
// not necessarily multiple - maybe just a single virtual ancestor or maybe a single
// non-public one
}
}
}
/**
* Use information from RTTI Base class Arrays to create class hierarchy lists and maps
* @throws CancelledException if cancelled
*/
private void createClassHierarchyListAndMapForGcc() throws CancelledException, Exception {
Iterator<RecoveredClass> recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
List<RecoveredClass> classHierarchyList = new ArrayList<RecoveredClass>();
// no parent case
if (nonInheritedGccClasses.contains(recoveredClass)) {
classHierarchyList = getGccNoClassHierarchy(recoveredClass);
recoveredClass.setClassHierarchy(classHierarchyList);
continue;
}
// case where there is all single inheritance in a class ancestry chain
if (singleInheritedGccClasses.contains(recoveredClass)) {
classHierarchyList = getGccSingleClassHierarchy(recoveredClass);
recoveredClass.setClassHierarchy(classHierarchyList);
continue;
}
}
recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
List<RecoveredClass> classHierarchyList = new ArrayList<RecoveredClass>();
// once all the non and single inheritance ones are created, create the multi ones
// case where there is multi-inheritance somewhere in the chain
if (multiAndOrVirtuallyInheritedGccClasses.contains(recoveredClass)) {
classHierarchyList = getGccMultiClassHierarchy(recoveredClass);
recoveredClass.setClassHierarchy(classHierarchyList);
}
}
// create parent class hierarchy maps
recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
List<RecoveredClass> parentList = recoveredClass.getParentList();
Iterator<RecoveredClass> parentIterator = parentList.iterator();
while (parentIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass parentClass = parentIterator.next();
recoveredClass.addClassHierarchyMapping(parentClass,
parentClass.getClassHierarchy());
}
}
// update the inherits virtual ancestor flag using ancestors - previously was only done for
// parents but now have all classes with flag set for direct parent so can get the other ancestors
// too
recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
// if we already know it then skip
if (recoveredClass.inheritsVirtualAncestor()) {
continue;
}
// if hasn't been set yet - check the other ancestors besides parents
if (hasVirtualAncestor(recoveredClass)) {
recoveredClass.setInheritsVirtualAncestor(true);
}
}
}
private boolean hasVirtualAncestor(RecoveredClass recoveredClass) throws CancelledException {
List<RecoveredClass> classHierarchy = recoveredClass.getClassHierarchy();
Iterator<RecoveredClass> classIterator = classHierarchy.iterator();
while (classIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass ancestor = classIterator.next();
if (ancestor.inheritsVirtualAncestor()) {
return true;
}
}
return false;
}
/**
* Create the class hierarchy list for a class with no inheritance
* @param recoveredClass the given class
* @return the class hierarchy list for the given class with no inheritance
*/
private List<RecoveredClass> getGccNoClassHierarchy(RecoveredClass recoveredClass) {
List<RecoveredClass> classHierarchyList = new ArrayList<RecoveredClass>();
classHierarchyList.add(recoveredClass);
return classHierarchyList;
}
/**
* Create the class hierarchy for a class with only single inheritance parents
* @param recoveredClass the given class
* @return the class hierarchy for the given class with only single inheritance parents
* @throws CancelledException if cancelled
*/
List<RecoveredClass> getGccSingleClassHierarchy(RecoveredClass recoveredClass)
throws CancelledException {
List<RecoveredClass> classHierarchyList = new ArrayList<RecoveredClass>();
RecoveredClass currentClass = recoveredClass;
classHierarchyList.add(currentClass);
while (currentClass.hasParentClass()) {
monitor.checkCanceled();
currentClass = currentClass.getParentList().get(0);
classHierarchyList.add(currentClass);
}
return classHierarchyList;
}
/**
* Create the class hierarchy list for a class with multiple inheritance
* @param recoveredClass the given class
* @return the class hierarchy list for the given class with multiple inheritance
* @throws CancelledException if cancelled
*/
List<RecoveredClass> getGccMultiClassHierarchy(RecoveredClass recoveredClass)
throws CancelledException {
List<RecoveredClass> classHierarchyList = new ArrayList<RecoveredClass>();
classHierarchyList.add(recoveredClass);
List<RecoveredClass> parentList = recoveredClass.getParentList();
Iterator<RecoveredClass> parentIterator = parentList.iterator();
while (parentIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass parentClass = parentIterator.next();
if (nonInheritedGccClasses.contains(parentClass)) {
classHierarchyList.addAll(parentClass.getClassHierarchy());
continue;
}
if (singleInheritedGccClasses.contains(parentClass)) {
classHierarchyList.addAll(parentClass.getClassHierarchy());
continue;
}
if (multiAndOrVirtuallyInheritedGccClasses.contains(parentClass)) {
classHierarchyList.addAll(getGccMultiClassHierarchy(parentClass));
}
}
return classHierarchyList;
}
/**
* Method to create a series of long data types from the given start address to the given end
* address
* @param start the starting address
* @param end the ending address
* @throws CancelledException if cancelled
* @throws Exception if data has conflict when created
*/
private void createLongs(Address start, Address end) throws CancelledException, Exception {
LongDataType longDT = new LongDataType();
int offset = 0;
Address address = start;
while (address != null && !address.equals(end)) {
api.clearListing(address, address.add(defaultPointerSize - 1));
api.createData(address, longDT);
offset += defaultPointerSize;
address = getAddress(start, offset);
}
}
/**
* Method to get address at address + offset
* @param address the given address
* @param offset the given offset
* @return the address at address + offset or null if it doesn't exist
*/
private Address getAddress(Address address, int offset) {
try {
Address newAddress = address.add(offset);
return newAddress;
}
catch (AddressOutOfBoundsException e) {
return null;
}
}
private int getNumFunctionPointers(Address topAddress, boolean allowNullFunctionPtrs,
boolean allowDefaultRefsInMiddle) throws CancelledException {
int numFunctionPointers = 0;
Address address = topAddress;
MemoryBlock currentBlock = program.getMemory().getBlock(topAddress);
boolean stillInCurrentTable = true;
while (address != null && currentBlock.contains(address) && stillInCurrentTable &&
(isPossibleFunctionPointer(address) ||
(allowNullFunctionPtrs && isPossibleNullPointer(address)))) {
numFunctionPointers++;
address = address.add(defaultPointerSize);
Symbol symbol = api.getSymbolAt(address);
if (symbol == null) {
continue;
}
// never let non-default refs in middle
if (symbol.getSource() != SourceType.DEFAULT) {
stillInCurrentTable = false;
}
// if it gets here it is default
if (!allowDefaultRefsInMiddle) {
stillInCurrentTable = false;
}
}
return numFunctionPointers;
}
/**
* Method to determine if there are enough zeros to make a null poihnter and no references into
* or out of the middle
* @param address the given address
* @return true if the given address could be a valid null pointer, false if not
*/
private boolean isPossibleNullPointer(Address address) throws CancelledException {
if (!extraUtils.hasNumZeros(address, defaultPointerSize)) {
return false;
}
return true;
}
/**
* Method to determine if the given address contains a possible function pointer
* @param address the given address
* @return true if the given address contains a possible function pointer or false otherwise
*/
private boolean isPossibleFunctionPointer(Address address) {
Address possibleFunctionPointer = extraUtils.getPointer(address);
if (possibleFunctionPointer == null) {
return false;
}
Function function = api.getFunctionAt(possibleFunctionPointer);
if (function != null) {
return true;
}
return false;
}
/**
* Method to call create and apply class structures method starting with top parent classes
* and non-virtual classes then the children and their children until all classes are processed.
* @throws CancelledException when cancelled
* @throws Exception if issue creating data
*/
private void createAndApplyClassStructures() throws CancelledException, Exception {
List<RecoveredClass> listOfClasses = new ArrayList<RecoveredClass>(recoveredClasses);
Iterator<RecoveredClass> recoveredClassIterator = recoveredClasses.iterator();
// first process all the classes with no parents
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
if (recoveredClass.hasMultipleInheritance()) {
continue;
}
if (recoveredClass.hasParentClass()) {
continue;
}
if (!recoveredClass.hasVftable()) {
createClassStructureWhenNoParentOrVftable(recoveredClass);
listOfClasses.remove(recoveredClass);
continue;
}
processDataTypes(recoveredClass);
listOfClasses.remove(recoveredClass);
}
// now process the classes that have all parents processed
// continue looping until all classes are processed
int numLoops = 0;
while (!listOfClasses.isEmpty()) {
monitor.checkCanceled();
// put in stop gap measure in case some classes never get all
// parents processed for some reason
if (numLoops == 100) {
return;
}
numLoops++;
recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
RecoveredClass recoveredClass = recoveredClassIterator.next();
monitor.checkCanceled();
if (!listOfClasses.contains(recoveredClass)) {
continue;
}
if (!allAncestorDataHasBeenCreated(recoveredClass)) {
continue;
}
processDataTypes(recoveredClass);
listOfClasses.remove(recoveredClass);
}
}
}
/**
* Method to create all the class data types for the current class, name all the class functions, and put them all into the class namespace
* @param recoveredClass current class
* @throws CancelledException when cancelled
* @throws Exception naming exception
*/
private void processDataTypes(RecoveredClass recoveredClass)
throws CancelledException, Exception {
// can't handle creating class data types for classes with virtual parents yet
if (recoveredClass.inheritsVirtualAncestor()) {
if (DEBUG) {
Msg.debug(this,
"Cannot create class data type for " +
recoveredClass.getClassNamespace().getName(true) +
" because it has virtual ancestors and we don't yet handle that use case.");
}
return;
}
// can't handle creating class data types for diamond shaped classes yet
if (recoveredClass.isDiamondShaped()) {
if (DEBUG) {
Msg.debug(this,
"Cannot create class data type for " +
recoveredClass.getClassNamespace().getName(true) +
" because it is diamond shaped and we don't yet handle that use case.");
}
return;
}
if (!recoveredClass.hasVftable()) {
createSimpleClassStructure(recoveredClass, null);
// return in this case 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;
}
// create pointers to empty vftable structs so they can be added to the class data type
// then filled in later
Map<Address, DataType> vfPointerDataTypes = createEmptyVfTableStructs(recoveredClass);
// create current class structure and add pointer to vftable, all parent member data strutures,
// and class member data structure
Structure classStruct = createSimpleClassStructure(recoveredClass, vfPointerDataTypes);
// Now that we have a class data type
// name constructor and destructor functions and put into the class namespace
addConstructorsToClassNamespace(recoveredClass, classStruct);
addDestructorsToClassNamespace(recoveredClass, classStruct);
//TODO:
// addNonThisDestructorsToClassNamespace(recoveredClass);
// addVbaseDestructorsToClassNamespace(recoveredClass);
// addVbtableToClassNamespace(recoveredClass);
//TODO:
// // add secondary label on functions with inlined constructors or destructors
// createInlinedConstructorComments(recoveredClass);
// createInlinedDestructorComments(recoveredClass);
// createIndeterminateInlineComments(recoveredClass);
// add label on constructor destructor functions that could not be determined which were which
createIndeterminateLabels(recoveredClass, classStruct);
// This is done after the class structure is created and added to the dtmanager
// because if done before the class structures are created
// then empty classes will get auto-created in the wrong place
// when the vfunctions are put in the class
fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes,
classStruct);
}
private Structure createSimpleClassStructure(RecoveredClass recoveredClass,
Map<Address, DataType> vfPointerDataTypes) throws Exception {
String className = recoveredClass.getName();
CategoryPath classPath = recoveredClass.getClassPath();
// get either existing structure if prog has a structure created by pdb or computed structure
// from decompiled construtor(s) info
Structure classStructure;
if (recoveredClass.hasExistingClassStructure()) {
classStructure = recoveredClass.getExistingClassStructure();
}
else {
classStructure = recoveredClass.getComputedClassStructure();
}
int structLen = 0;
if (classStructure != null) {
structLen = addAlignment(classStructure.getLength());
}
Structure classStructureDataType =
new StructureDataType(classPath, className, structLen, dataTypeManager);
// if no inheritance - add pointer to class vftable structure
if (nonInheritedGccClasses.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
Address vftableAddress = recoveredClass.getVftableAddresses().get(0);
DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress);
// simple case the offset for vftablePtr is 0
if (structUtils.canAdd(classStructureDataType, 0, classVftablePointer.getLength(),
monitor)) {
classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType,
0, classVftablePointer, 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
else {
Map<Integer, RecoveredClass> orderToParentMap =
classToParentOrderMap.get(recoveredClass);
if (orderToParentMap.isEmpty()) {
throw new Exception(
"Vmi class " + recoveredClass.getClassNamespace().getName(true) +
" should have a parent in the classToParentOrderMap but doesn't");
}
Map<RecoveredClass, Long> parentToOffsetMap =
classToParentOffsetMap.get(recoveredClass);
if (parentToOffsetMap.isEmpty()) {
throw new Exception(
"Vmi class " + recoveredClass.getClassNamespace().getName(true) +
" should have a parent in the classToParentOffsetMap but doesn't");
}
int numParents = orderToParentMap.keySet().size();
for (int i = 0; i < numParents; i++) {
RecoveredClass parent = orderToParentMap.get(i);
Long parentOffsetLong = parentToOffsetMap.get(parent);
if (parentOffsetLong == null) {
throw new Exception(
"Can't get parent offset for " + parent.getClassNamespace().getName(true));
}
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(parent.getClassNamespace().getName(true) +
" : structure should exist but doesn't.");
}
if (structUtils.canAdd(classStructureDataType, parentOffset,
baseClassStructure.getLength(), monitor)) {
classStructureDataType =
structUtils.addDataTypeToStructure(classStructureDataType, parentOffset,
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;
if (dataOffset != NONE) {
dataLen = structUtils.getNumberOfUndefinedsStartingAtOffset(classStructureDataType,
dataOffset, monitor);
}
if (dataLen != UNKNOWN && dataLen > 0) {
Structure recoveredClassDataStruct = createClassMemberDataStructure(recoveredClass,
classStructureDataType, dataLen, dataOffset);
if (recoveredClassDataStruct != null) {
classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType,
dataOffset, recoveredClassDataStruct, "data", monitor);
}
}
if (classStructureDataType.getNumComponents() == classStructureDataType
.getNumDefinedComponents()) {
classStructureDataType.setPackingEnabled(true);
}
classStructureDataType.setDescription(createParentStringBuffer(recoveredClass).toString());
classStructureDataType = (Structure) dataTypeManager.addDataType(classStructureDataType,
DataTypeConflictHandler.DEFAULT_HANDLER);
return classStructureDataType;
}
private boolean hasExternalBlock() {
MemoryBlock externalBlock = program.getMemory().getBlock("EXTERNAL");
if (externalBlock == null) {
return false;
}
return true;
}
}