GP-4723 - Clean up RTTI TypeDescriptorModel use of Demangler

This commit is contained in:
ghizard 2024-07-05 06:38:26 -04:00
parent d5cbda1e21
commit 98602916e4
3 changed files with 94 additions and 120 deletions

View file

@ -152,13 +152,14 @@ public class CreateTypeDescriptorBackgroundCmd
return false; return false;
} }
// If PDB had been run, then the namespace here might already have been promoted to
// a class type. At this point in processing, we know that the model only has a type
// with a "class" or "struct" tag (see TypeDescriptorModel).
// <br>Note: For now this assumes all classes and structs with RTTI data must // <br>Note: For now this assumes all classes and structs with RTTI data must
// actually be classes. In the future this might need additional checking before // actually be classes. In the future this might need additional checking before
// promoting some "struct" ref types to being a class, if we can better determine // promoting some "struct" ref types to being a class, if we can better determine
// whether or not they are actually classes. // whether or not they are actually classes.
String refType = model.getRefType(); // Can be null. if (!(classNamespace instanceof GhidraClass)) {
boolean makeClass = "class".equals(refType) || "struct".equals(refType);
if (makeClass) {
classNamespace = RttiUtil.promoteToClassNamespace(program, classNamespace); classNamespace = RttiUtil.promoteToClassNamespace(program, classNamespace);
} }

View file

@ -18,8 +18,7 @@ package ghidra.app.cmd.data;
import ghidra.app.cmd.data.rtti.RttiUtil; import ghidra.app.cmd.data.rtti.RttiUtil;
import ghidra.app.util.datatype.microsoft.DataValidationOptions; import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils; import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.app.util.demangler.DemangledObject; import ghidra.app.util.demangler.*;
import ghidra.app.util.demangler.DemangledType;
import ghidra.docking.settings.SettingsImpl; import ghidra.docking.settings.SettingsImpl;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -30,14 +29,12 @@ import ghidra.program.model.mem.Memory;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import mdemangler.*; import mdemangler.MDException;
import mdemangler.datatype.MDDataType; import mdemangler.MDMangGhidra;
import mdemangler.datatype.complex.MDComplexType;
import mdemangler.datatype.modifier.MDModifierType;
import mdemangler.naming.MDQualifiedName;
/** /**
* Model for the TypeDescriptor data type. * Model for the TypeDescriptor data type.
@ -57,7 +54,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
private boolean hasVFPointer; private boolean hasVFPointer;
private String originalTypeName; private String originalTypeName;
private MDComplexType mdComplexType; private DemangledDataType demangledDataType;
private boolean hasProcessedName = false; private boolean hasProcessedName = false;
private Namespace namespace; private Namespace namespace;
@ -65,6 +62,8 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
* Creates the model for the exception handling TypeDescriptor data type. * Creates the model for the exception handling TypeDescriptor data type.
* @param program the program * @param program the program
* @param address the address in the program for the TypeDescriptor data type. * @param address the address in the program for the TypeDescriptor data type.
* @param validationOptions options indicating how to validate the data type at the indicated
* address
*/ */
public TypeDescriptorModel(Program program, Address address, public TypeDescriptorModel(Program program, Address address,
DataValidationOptions validationOptions) { DataValidationOptions validationOptions) {
@ -204,8 +203,9 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
} }
/** /**
* Gets the TypeDescriptor structure for the indicated program. * Gets the TypeDescriptor structure for the indicated program
* @return the TypeDescriptor structure. * @param program the program which will contain this model's data type
* @return the TypeDescriptor structure
*/ */
public static DataType getDataType(Program program) { public static DataType getDataType(Program program) {
@ -247,9 +247,10 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
} }
/** /**
* Determine if this model's data type has a vf table pointer. * Determines if this model's data type program-wide TypeInfo Vftable pointer. This is used
* @param program the program which will contain this model's data type. * as an indication as to whether the particular data type has a Vftable pointer
* @return true if the data type has a vf table pointer. Otherwise, it has a hash value. * @param program the program which will contain this model's data type
* @return true if the data type has a vf table pointer. Otherwise, it has a hash value
*/ */
private static boolean hasVFPointer(Program program) { private static boolean hasVFPointer(Program program) {
@ -374,7 +375,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
*/ */
public Scalar getHashValue() throws InvalidDataTypeException, UndefinedValueException { public Scalar getHashValue() throws InvalidDataTypeException, UndefinedValueException {
checkValidity(); checkValidity();
if (hasVFPointer) { if (!hasVFPointer) {
throw new UndefinedValueException( throw new UndefinedValueException(
"No hash value is defined for this TypeDescriptor model."); "No hash value is defined for this TypeDescriptor model.");
} }
@ -429,6 +430,9 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
* model's address. * model's address.
*/ */
private String doGetTypeName() throws InvalidDataTypeException { private String doGetTypeName() throws InvalidDataTypeException {
if (hasProcessedName) {
return originalTypeName;
}
// last component is the type descriptor name. // last component is the type descriptor name.
Address nameAddress = getComponentAddressOfTypeName(); // Could be null. Address nameAddress = getComponentAddressOfTypeName(); // Could be null.
if (nameAddress == null) { if (nameAddress == null) {
@ -442,9 +446,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
Object value = terminatedStringDt.getValue(nameMemBuffer, SettingsImpl.NO_SETTINGS, 1); Object value = terminatedStringDt.getValue(nameMemBuffer, SettingsImpl.NO_SETTINGS, 1);
if (value instanceof String) { if (value instanceof String) {
originalTypeName = (String) value; originalTypeName = (String) value;
if (originalTypeName != null) { demangledDataType = getDemangledDataType(originalTypeName); // Can be null
mdComplexType = getMDComplexType(program, originalTypeName); // Can be null.
}
} }
hasProcessedName = true; hasProcessedName = true;
return originalTypeName; return originalTypeName;
@ -453,13 +455,13 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
private boolean hasComplexType() { private boolean hasComplexType() {
if (!hasProcessedName) { if (!hasProcessedName) {
try { try {
getTypeName(); // Initialize originalTypeName & mdComplexType if possible. getTypeName(); // Initialize originalTypeName & demangledDataType if possible.
} }
catch (InvalidDataTypeException e) { catch (InvalidDataTypeException e) {
return false; return false;
} }
} }
return (mdComplexType != null); return (demangledDataType != null);
} }
/** /**
@ -468,16 +470,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
* @return the full demangled type name or null. * @return the full demangled type name or null.
*/ */
public String getDemangledTypeDescriptor() { public String getDemangledTypeDescriptor() {
return hasComplexType() ? mdComplexType.toString() : null; return hasComplexType() ? demangledDataType.getOriginalDemangled() : null;
}
/**
* Gets the reference type of the type descriptor. (i.e. class, struct, union, enum)
* @return the type of thing referred to by this descriptor, or null if it couldn't be
* determined.
*/
public String getRefType() {
return hasComplexType() ? mdComplexType.getTypeName() : null;
} }
/** /**
@ -486,24 +479,15 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
* be determined. * be determined.
*/ */
public String getDescriptorName() { public String getDescriptorName() {
if (!hasComplexType()) { return hasComplexType() ? demangledDataType.getName() : null;
return null;
}
MDQualifiedName qualifiedName = mdComplexType.getNamespace();
return qualifiedName.getName();
} }
/** /**
* Gets the parent namespace of the type descriptor. * Gets the parent namespace of the type descriptor.
* @return the parent namespace as a DemangledType or null. * @return the parent namespace as a DemangledType or null.
*/ */
public DemangledType getParentNamespace() { public Demangled getParentNamespace() {
if (!hasComplexType()) { return hasComplexType() ? demangledDataType.getNamespace() : null;
return null;
}
MDQualifiedName qualifiedName = mdComplexType.getNamespace();
MDMangGhidra demangler = new MDMangGhidra();
return demangler.processNamespace(qualifiedName);
} }
/** /**
@ -512,7 +496,7 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
* @return the full pathname or null. * @return the full pathname or null.
*/ */
public String getDescriptorTypeNamespace() { public String getDescriptorTypeNamespace() {
return hasComplexType() ? mdComplexType.getTypeNamespace() : null; return hasComplexType() ? demangledDataType.getNamespaceString() : null;
} }
/** /**
@ -585,27 +569,20 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
} }
/** /**
* Gets the namespace for this descriptor. It will create the namespace if it doesn't already exist. * Gets the namespace for this descriptor. It will create the namespace if it doesn't already
* @return the descriptor's namespace, or null if it couldn't be determined. * exist
* @return the descriptor's namespace or null if it couldn't be determined
*/ */
public Namespace getDescriptorAsNamespace() { public Namespace getDescriptorAsNamespace() {
if (namespace == null || isNamespaceDeleted(namespace)) { if (namespace != null && !isNamespaceDeleted(namespace)) {
String descriptorName = getDescriptorName(); // Can be null. return namespace;
if (descriptorName == null) { }
if (hasComplexType() && demangledDataType == null) {
return null; return null;
} }
String demangledSource = mdComplexType.toString();
DemangledType typeNamespace =
new DemangledType(originalTypeName, demangledSource, descriptorName);
DemangledType parentNamespace = getParentNamespace(); // Can be null;
if (parentNamespace != null) {
typeNamespace.setNamespace(parentNamespace);
}
Program program = getProgram(); Program program = getProgram();
namespace = DemangledObject.createNamespace(program, typeNamespace, namespace = DemangledObject.createNamespace(program, demangledDataType,
program.getGlobalNamespace(), false); program.getGlobalNamespace(), false);
}
return namespace; return namespace;
} }
@ -618,27 +595,37 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
} }
/** /**
* Gets a demangler complex type for the indicated mangled string. * Gets a DemangledDataType for the indicated mangled string
* @param program the program containing the mangled string * @param mangledString the mangled string to be demangled
* @param mangledString the mangled string to be decoded * @return the DemangledDataType or null if couldn't demangle or is not a class type
* @return the associated complex type or null if the string couldn't be demangled.
*/ */
private static MDComplexType getMDComplexType(Program program, String mangledString) { private static DemangledDataType getDemangledDataType(String mangledString) {
MDMangGhidra demangler = new MDMangGhidra(); MDMangGhidra demangler = new MDMangGhidra();
try { try {
MDDataType mangledDt = demangler.demangleType(mangledString, true); // Note that we could play with the return value, but it is not needed; instead, we
if (mangledDt instanceof MDModifierType modifierType) { // get the DemangledDataType by calling the appropriate method
MDType refType = modifierType.getReferencedType(); demangler.demangleType(mangledString, true);
if (refType instanceof MDComplexType complexType) { DemangledDataType demangledType = demangler.getDataType();
return complexType; if (isPermittedType(demangledType)) {
return demangledType;
} }
} }
return null; // Not an MDComplexType
}
catch (MDException e) { catch (MDException e) {
// Couldn't demangle. // Couldn't demangle.
}
return null; return null;
} }
private static boolean isPermittedType(DemangledDataType demangledDataType) {
if (demangledDataType == null) {
return false;
}
if (demangledDataType.isClass() || demangledDataType.isStruct()) {
return true;
}
Msg.info(TypeDescriptorModel.class,
"Unprocessed TypeDescriptor: " + demangledDataType.getSignature());
return false;
} }
} }

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
import java.util.*; import java.util.*;
import ghidra.app.cmd.data.CreateTypeDescriptorBackgroundCmd; import ghidra.app.cmd.data.CreateTypeDescriptorBackgroundCmd;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.cmd.data.rtti.*; import ghidra.app.cmd.data.rtti.*;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.datatype.microsoft.*; import ghidra.app.util.datatype.microsoft.*;
@ -30,7 +29,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramMemoryUtil; import ghidra.program.util.ProgramMemoryUtil;
import ghidra.util.bytesearch.*; import ghidra.util.bytesearch.*;
import ghidra.util.exception.*; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/** /**
@ -76,7 +75,7 @@ public class RttiAnalyzer extends AbstractAnalyzer {
throws CancelledException { throws CancelledException {
// "rttiFound" option added in 10.3 so if analyzed with previous version analyzer will rerun // "rttiFound" option added in 10.3 so if analyzed with previous version analyzer will rerun
if(hasRun(program)) { if (hasRun(program)) {
return true; return true;
} }
@ -87,7 +86,7 @@ public class RttiAnalyzer extends AbstractAnalyzer {
return true; return true;
} }
RttiUtil.createTypeInfoVftableSymbol(program,commonVfTableAddress); RttiUtil.createTypeInfoVftableSymbol(program, commonVfTableAddress);
Set<Address> possibleTypeAddresses = locatePotentialRTTI0Entries(program, set, monitor); Set<Address> possibleTypeAddresses = locatePotentialRTTI0Entries(program, set, monitor);
if (possibleTypeAddresses == null) { if (possibleTypeAddresses == null) {
@ -111,7 +110,7 @@ public class RttiAnalyzer extends AbstractAnalyzer {
private boolean hasRun(Program program) { private boolean hasRun(Program program) {
Options programOptions = program.getOptions(Program.PROGRAM_INFO); Options programOptions = program.getOptions(Program.PROGRAM_INFO);
Boolean hasRun = (Boolean) programOptions.getObject(RTTI_FOUND_OPTION, null); Boolean hasRun = (Boolean) programOptions.getObject(RTTI_FOUND_OPTION, null);
if(hasRun == null) { if (hasRun == null) {
return false; return false;
} }
return true; return true;
@ -164,27 +163,14 @@ public class RttiAnalyzer extends AbstractAnalyzer {
monitor.checkCancelled(); monitor.checkCancelled();
monitor.setProgress(count++); monitor.setProgress(count++);
// Validate
TypeDescriptorModel typeModel =
new TypeDescriptorModel(program, rtti0Address, validationOptions);
try {
// Check that name matches the expected format.
String typeName = typeModel.getTypeName(); // can be null.
if (typeName == null || !typeName.startsWith(CLASS_PREFIX_CHARS)) {
continue; // Invalid so don't create.
}
}
catch (InvalidDataTypeException e) {
continue; // Invalid so don't create.
}
// Create the TypeDescriptor (RTTI 0) regardless of the other RTTI structures. // Create the TypeDescriptor (RTTI 0) regardless of the other RTTI structures.
CreateTypeDescriptorBackgroundCmd typeDescCmd = new CreateTypeDescriptorBackgroundCmd( CreateTypeDescriptorBackgroundCmd typeDescCmd = new CreateTypeDescriptorBackgroundCmd(
rtti0Address, validationOptions, applyOptions); rtti0Address, validationOptions, applyOptions);
typeDescCmd.applyTo(program, monitor); // Could call typeDescCmd.getStatusMsg() on failure
if (typeDescCmd.applyTo(program, monitor)) {
rtti0Locations.add(rtti0Address); rtti0Locations.add(rtti0Address);
} }
}
// Create any valid RTTI4s for this TypeDescriptor // Create any valid RTTI4s for this TypeDescriptor
processRtti4sForRtti0(program, rtti0Locations, monitor); processRtti4sForRtti0(program, rtti0Locations, monitor);