GP-1994 refined datatype search and add ability to specify a program's

preferred root-namespace category node
This commit is contained in:
ghidra1 2022-05-06 15:11:39 -04:00
parent 4b600847eb
commit d7fc209657
12 changed files with 734 additions and 179 deletions

View file

@ -19,6 +19,8 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import db.DBConstants;
import db.DBHandle;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
@ -46,6 +48,7 @@ import ghidra.program.database.reloc.RelocationManager;
import ghidra.program.database.symbol.*;
import ghidra.program.database.util.AddressSetPropertyMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
@ -208,6 +211,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
private Map<String, AddressSetPropertyMapDB> addrSetPropertyMap = new HashMap<>();
private Map<String, IntRangeMapDB> intRangePropertyMap = new HashMap<>();
// cached program information properties
private static final String PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY_PATHNAME =
PROGRAM_INFO + "." + PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY;
private CategoryPath preferredRootNamespaceCategory; // value of PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY
/**
* Constructs a new ProgramDB
* @param name the name of the program
@ -247,7 +255,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
listing = new ListingDB();
changeSet = new ProgramDBChangeSet(addrMap, NUM_UNDOS);
initManagers(CREATE, TaskMonitor.DUMMY);
propertiesCreate();
createProgramInformationOptions();
programUserData = new ProgramUserDataDB(this);
endTransaction(id, true);
clearUndo(false);
@ -369,7 +377,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
changed = true;
}
propertiesRestore();
registerProgramInformationOptions();
restoreProgramInformationOptions();
recordChanges = true;
endTransaction(id, true);
clearUndo(false);
@ -433,25 +442,59 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
recordChanges = true;
}
private void propertiesRestore() {
private void registerProgramInformationOptions() {
Options pl = getOptions(PROGRAM_INFO);
boolean origChangeState = changed;
pl.registerOption(EXECUTABLE_PATH, UNKNOWN, null, "Original import path of program image");
pl.registerOption(EXECUTABLE_FORMAT, UNKNOWN, null, "Original program image format");
pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "3.0 or earlier", null,
"Version of Ghidra used to create this program.");
pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created");
changed = origChangeState;
pl.registerOption(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY, "", null,
"Preferred data type category path of root namespace");
}
private void propertiesCreate() {
private void createProgramInformationOptions() {
registerProgramInformationOptions();
Options pl = getOptions(PROGRAM_INFO);
boolean origChangeState = changed;
pl.setString(EXECUTABLE_PATH, UNKNOWN);
pl.setString(EXECUTABLE_FORMAT, UNKNOWN);
pl.setString(CREATED_WITH_GHIDRA_VERSION, Application.getApplicationVersion());
pl.setDate(DATE_CREATED, new Date());
changed = origChangeState;
}
private void restoreProgramInformationOptions() {
Options pl = getOptions(PROGRAM_INFO);
updatePreferredRootNamespaceCategory(
pl.getString(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY, null));
}
protected boolean propertyChanged(String propertyName, Object oldValue, Object newValue) {
if (propertyName.equals(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY_PATHNAME)) {
String path = (String) newValue;
if (!updatePreferredRootNamespaceCategory(path)) {
return false;
}
}
super.propertyChanged(propertyName, oldValue, newValue);
return true;
}
private boolean updatePreferredRootNamespaceCategory(String path) {
if (path != null) {
path = path.trim();
}
preferredRootNamespaceCategory = null;
if (!StringUtils.isBlank(path)) {
try {
preferredRootNamespaceCategory = new CategoryPath(path);
}
catch (Exception e) {
// ignore invalid path
Msg.error(this, "Ignoring invalid preferred root namespace category path: " + path);
return false;
}
}
return true;
}
void setProgramUserData(ProgramUserDataDB programUserData) {
@ -556,7 +599,17 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setCompiler(String compiler) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(COMPILER, compiler);
changed = true;
}
@Override
public CategoryPath getPreferredRootNamespaceCategoryPath() {
return preferredRootNamespaceCategory;
}
@Override
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY, categoryPath);
}
@Override
@ -571,7 +624,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutablePath(String path) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_PATH, path);
changed = true;
}
@Override
@ -591,7 +643,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutableFormat(String format) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_FORMAT, format);
changed = true;
}
@Override
@ -611,7 +662,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutableMD5(String md5) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_MD5, md5);
changed = true;
}
@Override
@ -631,7 +681,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutableSHA256(String sha256) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_SHA256, sha256);
changed = true;
}
@Override
@ -1738,6 +1787,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
for (int i = 0; i < NUM_MANAGERS; i++) {
managers[i].invalidateCache(all);
}
restoreProgramInformationOptions();
installExtensions(); // Reload any extensions
}
catch (IOException e) {

View file

@ -19,11 +19,11 @@ import java.util.*;
import java.util.regex.Pattern;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPathParser;
import ghidra.docking.settings.Settings;
import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Library;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
@ -354,11 +354,37 @@ public class DataTypeUtilities {
: new CategoryPath(baseCategory, categoryPathParts);
}
/**
* Find the structure data type which corresponds to the specified class namespace
* within the specified data type manager.
* The structure must utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the full class namespace.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
* @param dataTypeManager data type manager which should be searched.
* @param classNamespace class namespace
* @return existing structure which resides within matching category.
*/
public static Structure findExistingClassStruct(DataTypeManager dataTypeManager, GhidraClass classNamespace) {
Structure dt = findPreferredDataType(dataTypeManager, classNamespace,
classNamespace.getName(), Structure.class, true);
if (dt != null) {
return dt;
}
final String[] namespacePaths = getRelativeCategoryPaths(classNamespace);
return findDataType(dataTypeManager, classNamespace.getName(), Structure.class,
categoryPath -> getCategoryMatchType(categoryPath, namespacePaths, true));
}
/**
* Attempt to find the data type whose dtName and specified namespace match a stored data type
* within the specified dataTypeManager. The best match will be returned. The namespace will be
* used in checking data type parent categories, however if no type corresponds to the namespace
* another type whose name matches may be returned.
* within the specified dataTypeManager. The first match which satisfies the category path
* requirement will be returned. If a non-root namespace is specified the datatype's trailing
* category path must match the specified namespace path.
*
* @param dataTypeManager data type manager
* @param namespace namespace associated with dtName (null indicates no namespace constraint)
@ -366,18 +392,26 @@ public class DataTypeUtilities {
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
* @return best matching data type
*/
public static DataType findDataType(DataTypeManager dataTypeManager, Namespace namespace,
String dtName, Class<? extends DataType> classConstraint) {
public static <T extends DataType> T findDataType(DataTypeManager dataTypeManager,
Namespace namespace, String dtName, Class<T> classConstraint) {
T dt =
findPreferredDataType(dataTypeManager, namespace, dtName, classConstraint, false);
if (dt != null) {
return dt;
}
final String[] namespacePaths = getRelativeCategoryPaths(namespace);
return findDataType(dataTypeManager, dtName, classConstraint,
categoryPath -> hasPreferredNamespaceCategory(categoryPath, namespace));
categoryPath -> getCategoryMatchType(categoryPath, namespacePaths, false));
}
/**
* Attempt to find the data type whose dtNameWithNamespace match a stored data type within the
* specified dataTypeManager. The best match will be returned. The namespace will be used in
* checking data type parent categories, however if no type corresponds to the namespace another
* type whose name matches may be returned. NOTE: name parsing assumes :: delimiter and can be
* thrown off if name include template information which could contain namespaces.
* specified dataTypeManager. The namespace will be used in checking data type parent categories.
* NOTE: name parsing assumes :: namespace delimiter which can be thrown off if name includes
* template information which could contain namespaces (see {@link SymbolPathParser#parse(String)}).
*
* @param dataTypeManager data type manager
* @param dtNameWithNamespace name of data type qualified with namespace (e.g.,
@ -385,14 +419,34 @@ public class DataTypeUtilities {
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
* @return best matching data type
*/
public static DataType findNamespaceQualifiedDataType(DataTypeManager dataTypeManager,
String dtNameWithNamespace, Class<? extends DataType> classConstraint) {
public static <T extends DataType> T findNamespaceQualifiedDataType(
DataTypeManager dataTypeManager,
String dtNameWithNamespace, Class<T> classConstraint) {
String[] splitName = dtNameWithNamespace.split(Namespace.DELIMITER);
String dtName = splitName[splitName.length - 1];
List<String> pathList = SymbolPathParser.parse(dtNameWithNamespace);
int nameIndex = pathList.size() - 1;
String dtName = pathList.get(nameIndex);
CategoryPath rootPath = getPreferredRootNamespaceCategoryPath(dataTypeManager);
if (rootPath != null) {
List<String> namespacePath = pathList.subList(0, nameIndex);
T dt = getAssignableDataType(dataTypeManager, rootPath, namespacePath, dtName,
classConstraint);
if (dt != null) {
return dt;
}
}
// generate namespace path with / instead of :: separators
StringBuilder buf = new StringBuilder();
for (int i = 0; i < nameIndex; i++) {
buf.append(CategoryPath.DELIMITER_STRING);
buf.append(pathList.get(i));
}
final String namespacePath = buf.toString(); // root path will have empty string
return findDataType(dataTypeManager, dtName, classConstraint,
dataType -> hasPreferredNamespaceCategory(dataType, splitName));
categoryPath -> getCategoryMatchType(categoryPath, namespacePath));
}
/**
@ -410,40 +464,92 @@ public class DataTypeUtilities {
return cPrimitiveNameMap.get(dataTypeName);
}
private static boolean hasPreferredNamespaceCategory(DataType dataType,
String[] splitDataTypeName) {
// last element of split array is data type name and is ignored here
if (splitDataTypeName.length == 1) {
return true;
private static final int NAMESPACE_PATH_INDEX = 0;
private static final int PARENT_NAMESPACE_PATH_INDEX = 1;
/**
* Get relative/partial category paths which corresponds to a specified namespace.
* Any {@link Library} namespace will be ignored and treated like the global namespace
* when generating a related category path. An empty string will be returned for the
* global namespace.
* @param namespace data type namespace
* @return partial two-element array with category path for namespace [NAMESPACE_PATH_INDEX]
* and parent-namespace [PARENT_NAMESPACE_PATH_INDEX].
* A null is returned if namespace is null or the root/global namespace.
*/
private static String[] getRelativeCategoryPaths(Namespace namespace) {
if (namespace == null || namespace.isGlobal() || namespace.isLibrary()) {
return null;
}
CategoryPath categoryPath = dataType.getCategoryPath();
int index = splitDataTypeName.length - 2;
while (index >= 0) {
if (categoryPath.equals(CategoryPath.ROOT) ||
!categoryPath.getName().equals(splitDataTypeName[index])) {
return false;
}
categoryPath = categoryPath.getParent();
--index;
String[] paths = new String[2];
StringBuilder buf = new StringBuilder();
for (String n : namespace.getParentNamespace().getPathList(true)) {
buf.append(CategoryPath.DELIMITER_STRING);
buf.append(n);
}
return true;
paths[PARENT_NAMESPACE_PATH_INDEX] = buf.toString();
buf.append(CategoryPath.DELIMITER_STRING);
buf.append(namespace.getName());
paths[NAMESPACE_PATH_INDEX] = buf.toString();
return paths;
}
private static boolean hasPreferredNamespaceCategory(DataType dataType, Namespace namespace) {
if (namespace == null) {
return true;
private enum CategoryMatchType {
NONE, SECONDARY, PREFERRED;
}
/**
* Namespace category matcher. Only those datatypes contained within a catgeory
* whose trailing category path matches the specified namespacePath will be considered
* a possible match. If the namespacePath is empty array all category paths will
* be considered a match with preference given to the root category.
* @param categoryPath datatype category path
* @param namespacePath namespace path
* @return {@link CategoryMatchType#PREFERRED} if namespace match found, {@link CategoryMatchType#SECONDARY}
* if no namespace constraint specified else {@link CategoryMatchType#NONE} if namespace constraint not
* satisfied.
*/
private static CategoryMatchType getCategoryMatchType(CategoryPath categoryPath,
String namespacePath) {
if (namespacePath.length() == 0) {
// root or unspecified namespace - prefer root category
return categoryPath.isRoot() ? CategoryMatchType.PREFERRED : CategoryMatchType.SECONDARY;
}
CategoryPath categoryPath = dataType.getCategoryPath();
Namespace ns = namespace;
while (!(ns instanceof GlobalNamespace) && !(ns instanceof Library)) {
if (categoryPath.equals(CategoryPath.ROOT) ||
!categoryPath.getName().equals(ns.getName())) {
return false;
}
categoryPath = categoryPath.getParent();
ns = ns.getParentNamespace();
String path = categoryPath.getPath();
return path.endsWith(namespacePath) ? CategoryMatchType.PREFERRED : CategoryMatchType.NONE;
}
/**
* Namespace category matcher.
* @param categoryPath datatype category path
* @param namespacePaths namespace paths constraint or null for no namespace. This value should
* be obtained from the {@link #getRelativeCategoryPaths(Namespace)} method.
* @param parentNamespacePreferred if true matching on parent namespace is
* enabled and preferred over match on actual namespace. This is used for
* class structure searching.
* @return {@link CategoryMatchType#PREFERRED} is returned if parentNamespacePreferred is true
* and category path matches on parent-namespace or parentNamespacePreferred is false
* and category path matches on namespace. {@link CategoryMatchType#SECONDARY} is returned
* if parentNamespacePreferred is true and category path matches on namespace. Otherwise
* {@link CategoryMatchType#NONE} is returned.
*/
private static CategoryMatchType getCategoryMatchType(CategoryPath categoryPath,
String[] namespacePaths, boolean parentNamespacePreferred) {
if (namespacePaths == null) {
// root or unspecified namespace - prefer root category
return categoryPath.isRoot() ? CategoryMatchType.PREFERRED : CategoryMatchType.SECONDARY;
}
return true;
String path = categoryPath.getPath();
if (parentNamespacePreferred &&
path.endsWith(namespacePaths[PARENT_NAMESPACE_PATH_INDEX])) {
return CategoryMatchType.PREFERRED;
}
if (path.endsWith(namespacePaths[NAMESPACE_PATH_INDEX])) {
return parentNamespacePreferred ? CategoryMatchType.SECONDARY
: CategoryMatchType.PREFERRED;
}
return CategoryMatchType.NONE;
}
/**
@ -451,38 +557,170 @@ public class DataTypeUtilities {
* preferred namespace.
*/
private static interface NamespaceMatcher {
boolean isNamespaceCategoryMatch(DataType dataType);
/**
* Score category path match.
* @param path category path
* @return path match type
*/
CategoryMatchType getMatchType(CategoryPath path);
}
private static DataType findDataType(DataTypeManager dataTypeManager, String dtName,
Class<? extends DataType> classConstraint, NamespaceMatcher preferredCategoryMatcher) {
private static CategoryPath getPreferredRootNamespaceCategoryPath(
DataTypeManager dataTypeManager) {
if (!(dataTypeManager instanceof ProgramBasedDataTypeManager)) {
return null;
}
ProgramBasedDataTypeManager pdtm = (ProgramBasedDataTypeManager) dataTypeManager;
Program p = pdtm.getProgram();
return p.getPreferredRootNamespaceCategoryPath();
}
/**
* Get the specified datatype by full path and return only if its type corresponds to class
* constraint if specified.
* @param <T> A standard interface which extends {@link DataType} (e.g., {@link Structure}).
* @param dataTypeManager datatype manager to query
* @param rootPath root category path
* @param namespacePath an optional namespace path to be checked under rootPath.
* If null or empty the rootPath will be checked for dtName.
* @param dtName datatype name
* @param classConstraint datatype class constraint (optional, may be null)
* @return datatype which corresponds to specified path or null if not found
*/
private static <T extends DataType> T getAssignableDataType(DataTypeManager dataTypeManager,
CategoryPath rootPath, List<String> namespacePath, String dtName,
Class<? extends DataType> classConstraint) {
Category category = dataTypeManager.getCategory(rootPath);
if (category == null) {
return null;
}
if (namespacePath == null || namespacePath.isEmpty()) {
return getAssignableDataType(category, dtName, classConstraint);
}
CategoryPath categoryPath = new CategoryPath(rootPath, namespacePath);
category = dataTypeManager.getCategory(categoryPath);
if (category == null) {
return null;
}
return getAssignableDataType(category, dtName, classConstraint);
}
/**
* Get the specified datatype by name and category and return only if its type
* corresponds to an class constraint if specified.
* @param <T> A standard interface which extends {@link DataType} (e.g., {@link Structure}).
* @param category datatype category to query
* @param dtName datatype name
* @param classConstraint datatype class constraint (optional, may be null)
* @return datatype which corresponds to specified path or null if not found
*/
@SuppressWarnings("unchecked")
private static <T extends DataType> T getAssignableDataType(Category category, String dtName,
Class<? extends DataType> classConstraint) {
DataType dt = category.getDataType(dtName);
if (dt != null &&
(classConstraint == null || classConstraint.isAssignableFrom(dt.getClass()))) {
return (T) dt;
}
return null;
}
/**
* Perform a preferred category namespace qualified datatype search using
* category path supplied by {@link Program#getPreferredRootNamespaceCategoryPath()}.
* Any {@link Library} namespace will be ignored and treated like the global namespace
* when generating a related category path. This method only applies to
* {@link ProgramBasedDataTypeManager} and will always return null for other
* datatype managers.
* @param dataTypeManager datatype manager
* @param namespace namespace constraint or null for no namespace.
* @param dtName datatype name
* @param classConstraint type of datatype by its interface class (e.g., {@link Structure}).
* @param parentNamespacePreferred if true matching on parent namespace is
* enabled and preferred over match on actual namespace. This is relavent for
* class structure searching.
* @return preferred datatype match if found
*/
private static <T extends DataType> T findPreferredDataType(DataTypeManager dataTypeManager,
Namespace namespace, String dtName, Class<T> classConstraint,
boolean parentNamespacePreferred) {
CategoryPath rootPath = getPreferredRootNamespaceCategoryPath(dataTypeManager);
if (rootPath == null) {
return null;
}
if (namespace == null || namespace.isGlobal() || namespace.isLibrary()) {
return getAssignableDataType(dataTypeManager, rootPath, null, dtName, classConstraint);
}
if (parentNamespacePreferred) {
T dt = getAssignableDataType(dataTypeManager, rootPath,
namespace.getParentNamespace().getPathList(true), dtName, classConstraint);
if (dt != null) {
return dt;
}
}
return getAssignableDataType(dataTypeManager, rootPath, namespace.getPathList(true), dtName,
classConstraint);
}
/**
* Compare datatype category path lengths for sorting shortest path first.
* Tie-breaker based on path name sort.
* Rationale is to provide some deterministic datatype selection behavior and
* to allow duplicates within a hierarchical orgainzation to prefer the short
* path to reduce bad namespace matches.
*/
private static final Comparator<DataType> DATATYPE_CATEGORY_PATH_LENGTH_COMPARATOR =
(DataType dt1, DataType dt2) -> {
String catPath1 = dt1.getCategoryPath().getPath();
String catPath2 = dt2.getCategoryPath().getPath();
int cmp = catPath1.length() - catPath2.length();
if (cmp == 0) {
cmp = catPath1.compareTo(catPath2);
}
return cmp;
};
/**
* Perform a namespace qualified datatype search.
* @param dataTypeManager datatype manager
* @param dtName datatype name
* @param classConstraint type of datatype by its interface class (e.g., {@link Structure}).
* @param categoryMatcher responsible for evaluating the category path
* for a possible match with a namespace constraint.
* @return The first {@link CategoryMatchType#PREFERRED} match will be
* returned if found. If none are {@link CategoryMatchType#PREFERRED}, the first
* {@link CategoryMatchType#SECONDARY} match will be returned. Otherwise null is returned.
*/
@SuppressWarnings("unchecked")
private static <T extends DataType> T findDataType(DataTypeManager dataTypeManager,
String dtName, Class<T> classConstraint, NamespaceMatcher categoryMatcher) {
ArrayList<DataType> list = new ArrayList<>();
dataTypeManager.findDataTypes(dtName, list);
Collections.sort(list, DATATYPE_CATEGORY_PATH_LENGTH_COMPARATOR);
if (!list.isEmpty()) {
//use the datatype that exists in the root category,
//otherwise just pick the first one...
DataType anyDt = null;
DataType preferredDataType = null;
T secondaryMatch = null;
for (DataType existingDT : list) {
if (classConstraint != null &&
!classConstraint.isAssignableFrom(existingDT.getClass())) {
continue;
}
if (preferredCategoryMatcher == null) {
if (existingDT.getCategoryPath().equals(CategoryPath.ROOT)) {
return existingDT;
}
CategoryMatchType matchType =
categoryMatcher.getMatchType(existingDT.getCategoryPath());
if (matchType == CategoryMatchType.PREFERRED) {
return (T) existingDT; // preferred match
}
if (preferredCategoryMatcher.isNamespaceCategoryMatch(existingDT)) {
preferredDataType = existingDT;
else if (secondaryMatch == null && matchType == CategoryMatchType.SECONDARY) {
secondaryMatch = (T) existingDT;
}
// If all else fails return any matching name for backward compatibility
anyDt = existingDT;
}
if (preferredDataType != null) {
return preferredDataType;
}
return anyDt;
return secondaryMatch;
}
return null;
}

View file

@ -19,6 +19,7 @@ import java.util.Date;
import ghidra.framework.store.LockException;
import ghidra.program.database.IntRangeMap;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
@ -60,6 +61,9 @@ public interface Program extends DataTypeManagerDomainObject {
public static final String DATE_CREATED = "Date Created";
/** Name of ghidra version property */
public static final String CREATED_WITH_GHIDRA_VERSION = "Created With Ghidra Version";
/** Name of ghidra preferred root namespace category property */
public static final String PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY =
"Preferred Root Namespace Category";
/** Creation date to ask for analysis */
public static final String ANALYSIS_START_DATE = "2007-Jan-01";
/** Format string of analysis date */
@ -160,6 +164,37 @@ public interface Program extends DataTypeManagerDomainObject {
*/
public void setCompiler(String compiler);
/**
* Gets the preferred root data type category path which corresponds
* to the global namespace of a namespace-based storage area. Preference
* will be given to this category when searching for data types
* within a specific namespace.
*
* This setting corresponds to the Program Information option
* <i>"Preferred Root Namespace Category</i>. See {@link DataTypeUtilities}
* and its various find methods for its usage details.
*
* @return data type category path for root namespace or null if not set or is invalid.
*/
public CategoryPath getPreferredRootNamespaceCategoryPath();
/**
* Sets the preferred data type category path which corresponds
* to the root of a namespace hierarchy storage area. Preference
* will be given to this category when searching for data types
* within a specific namespace.
*
* This setting corresponds to the Program Information option
* <i>"Preferred Root Namespace Category</i>. See {@link DataTypeUtilities}
* and its various find methods for its usage details.
*
* @param categoryPath data type category path for root namespace or null
* to clear option. The specified path must be absolute and start with "/"
* and must not end with one (e.g., <i>/ClassDataTypes</i>). An invalid
* path setting will be ignored.
*/
public void setPreferredRootNamespaceCategoryPath(String categoryPath);
/**
* Gets the path to the program's executable file.
* For example, <code>C:\Temp\test.exe</code>.

View file

@ -43,7 +43,7 @@ public class VariableUtilities {
/**
* Get a precedence value for the specified variable.
* This value can be used to assist with LocalVariable.compareTo(Variable var)
* @param var
* @param var function variable
* @return numeric precedence
*/
public static int getPrecedence(Variable var) {
@ -76,8 +76,8 @@ public class VariableUtilities {
* Compare storage varnodes for two lists of variables. No check is done to ensure that
* storage is considered good/valid (i.e., BAD_STORAGE, UNASSIGNED_STORAGE and VOID_STORAGE
* all have an empty varnode list and would be considered a match)
* @param vars
* @param otherVars
* @param vars function variables
* @param otherVars other function variables
* @return true if the exact sequence of variable storage varnodes matches across two lists of variables.
*/
public static boolean storageMatches(List<Variable> vars, List<Variable> otherVars) {
@ -96,8 +96,8 @@ public class VariableUtilities {
* Compare storage varnodes for two lists of variables. No check is done to ensure that
* storage is considered good/valid (i.e., BAD_STORAGE, UNASSIGNED_STORAGE and VOID_STORAGE
* all have an empty varnode list and would be considered a match)
* @param vars
* @param otherVars
* @param vars function variables
* @param otherVars other function variables
* @return true if the exact sequence of variable storage varnodes matches across two lists of variables.
*/
public static boolean storageMatches(List<? extends Variable> vars, Variable... otherVars) {
@ -116,8 +116,8 @@ public class VariableUtilities {
/**
* Compare two variables without using the instance specific compareTo method.
* @param v1
* @param v2
* @param v1 a function variable
* @param v2 another function variable
* @return a negative value if v1 &lt; v2, 0 if equal, and
* positive if v1 &gt; v2
*/
@ -162,8 +162,8 @@ public class VariableUtilities {
/**
* Determine the appropriate data type for an automatic parameter
* @param function
* @param returnDataType
* @param function function whose auto param datatype is to be determined
* @param returnDataType function's return datatype
* @param storage variable storage for an auto-parameter (isAutoStorage should be true)
* @return auto-parameter data type
*/
@ -174,7 +174,7 @@ public class VariableUtilities {
if (autoParameterType == AutoParameterType.THIS) {
DataType classStruct = findOrCreateClassStruct(function);
if (classStruct == null) {
classStruct = DataType.VOID;
classStruct = VoidDataType.dataType;
}
return getPointer(function.getProgram(), classStruct, storage.size());
}
@ -197,7 +197,7 @@ public class VariableUtilities {
* @param storage variable storage whose size must match the specified data type size
* @param dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
* @param allowSizeMismatch if true size mismatch will be ignore
* @throws InvalidInputException
* @throws InvalidInputException if specified storage is not suitable for datatype
*/
public static void checkStorage(VariableStorage storage, DataType dataType,
boolean allowSizeMismatch) throws InvalidInputException {
@ -212,7 +212,7 @@ public class VariableUtilities {
* @param dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
* @param allowSizeMismatch if true size mismatch will be ignore
* @return original storage or resized storage with the correct size.
* @throws InvalidInputException
* @throws InvalidInputException if specified storage is not suitable for datatype
*/
public static VariableStorage checkStorage(Function function, VariableStorage storage,
DataType dataType, boolean allowSizeMismatch) throws InvalidInputException {
@ -363,10 +363,10 @@ public class VariableUtilities {
* Perform resize variable storage to desired newSize. This method has limited ability to grow
* storage if current storage does not have a stack component or if other space constraints
* are exceeded.
* @param curStorage
* @param dataType
* @param curStorage current variable storage
* @param dataType variable datatype
* @param alignStack if false no attempt is made to align stack usage for big-endian
* @param function
* @param function function which corresponds to resized variable storage
* @return resize storage
* @throws InvalidInputException if unable to resize storage to specified size.
*/
@ -572,10 +572,10 @@ public class VariableUtilities {
/**
* Check for variable storage conflict and optionally remove conflicting variables.
* @param function
* @param function function which corresponds to specified variable
* @param var existing function variable or null for new variable
* @param newStorage new/updated variable storage
* @param deleteConflictingVariables
* @param deleteConflictingVariables if true function's conflicting variables may be deleted
* @throws VariableSizeException if deleteConflictingVariables is false and another variable conflicts
*/
public static void checkVariableConflict(Function function, Variable var,
@ -616,9 +616,9 @@ public class VariableUtilities {
/**
* Check for variable storage conflict and optionally remove conflicting variables.
* @param existingVariables variables to check (may contain null entries)
* @param var
* @param newStorage
* @throws VariableSizeException
* @param var function variable
* @param conflictHandler variable conflict handler
* @param newStorage variable storage
* @throws VariableSizeException if another variable conflicts
*/
public static void checkVariableConflict(List<? extends Variable> existingVariables,
@ -685,7 +685,7 @@ public class VariableUtilities {
/**
* Determine the minimum stack offset for parameters
* @param function
* @param function function whose stack use is to be examined
* @return stack parameter offset or null if it could not be determined
*/
public static Integer getBaseStackParamOffset(Function function) {
@ -712,7 +712,8 @@ public class VariableUtilities {
/**
* Generate a suitable 'this' parameter for the specified function
* @param function
* @param function function for which a <code>this</code> parameter is to be generated
* @param convention function calling convention
* @return this parameter or null of calling convention is not a 'thiscall'
* or some other error prevents it
* @deprecated should rely on auto-param instead - try not to use this method which may be eliminated
@ -769,10 +770,18 @@ public class VariableUtilities {
/**
* Find the structure data type which corresponds to the specified class namespace
* within the specified data type manager.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* While a new empty structure may be returned, it will not be added to the program's data type
* manager.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* If a match is not found an empty placeholder structure will be instantiated
* and returned. A newly instantiated structure will not be added to the data type manager
* and may refer to a non-existing category path which corresponds to the class-parent's
* namespace.
*
* @param classNamespace class namespace
* @param dataTypeManager data type manager which should be searched and whose
* data organization should be used.
@ -789,9 +798,19 @@ public class VariableUtilities {
/**
* Find the structure data type which corresponds to the specified function's class namespace
* within the function's program. One will be instantiated if not found.
* within the function's program.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* If a match is not found an empty placeholder structure will be instantiated
* and returned. A newly instantiated structure will not be added to the data type manager
* and may refer to a non-existing category path which corresponds to the class-parent's
* namespace.
*
* @param function function's whose class namespace is the basis for the structure
* @return new or existing structure whose name matches the function's class namespace or
* null if function not contained within a class namespace.
@ -807,9 +826,14 @@ public class VariableUtilities {
/**
* Find the structure data type which corresponds to the specified class namespace
* within the specified data type manager. .
* within the specified data type manager.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* @param classNamespace class namespace
* @param dataTypeManager data type manager which should be searched.
* @return existing structure whose name matches the specified class namespace
@ -817,15 +841,19 @@ public class VariableUtilities {
*/
public static Structure findExistingClassStruct(GhidraClass classNamespace,
DataTypeManager dataTypeManager) {
return (Structure) DataTypeUtilities.findDataType(dataTypeManager,
classNamespace.getParentNamespace(), classNamespace.getName(), Structure.class);
return DataTypeUtilities.findExistingClassStruct(dataTypeManager, classNamespace);
}
/**
* Find the structure data type which corresponds to the specified function's class namespace
* within the function's program.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* @param func the function.
* @return existing structure whose name matches the specified function's class namespace
* or null if not found.

View file

@ -15,6 +15,8 @@
*/
package ghidra.program.model.symbol;
import java.util.*;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.CircularDependencyException;
import ghidra.util.exception.DuplicateNameException;
@ -24,6 +26,7 @@ import ghidra.util.exception.InvalidInputException;
* The Namespace interface
*/
public interface Namespace {
static final long GLOBAL_NAMESPACE_ID = 0;
/**
* The delimiter that is used to separate namespace nodes in a namespace
@ -63,6 +66,24 @@ public interface Namespace {
*/
public String getName(boolean includeNamespacePath);
/**
* Get the namespace path as a list of namespace names.
* @param omitLibrary if true Library name (if applicable) will be
* omitted from returned list and treated same as global namespace.
* @return namespace path list or empty list for global namespace
*/
public default List<String> getPathList(boolean omitLibrary) {
if (isGlobal()) {
return Collections.emptyList();
}
ArrayDeque<String> list = new ArrayDeque<>();
for (Namespace n = this; !n.isGlobal() && !(omitLibrary && n.isLibrary()); n =
n.getParentNamespace()) {
list.addFirst(n.getName());
}
return List.copyOf(list);
}
/**
* Return the namespace id
* @return the namespace id
@ -96,11 +117,20 @@ public interface Namespace {
throws DuplicateNameException, InvalidInputException, CircularDependencyException;
/**
* Return true if this is the global namespace;
* @return true if this is the global namespace;
* Return true if this is the global namespace
* @return true if this is the global namespace
*/
public default boolean isGlobal() {
return getID() == GLOBAL_NAMESPACE_ID;
}
/**
* Return true if this is a library
* @return true if this is a library
*/
public default boolean isLibrary() {
Symbol s = getSymbol();
return s != null && s.getSymbolType() == SymbolType.LIBRARY;
}
}

View file

@ -26,6 +26,7 @@ import ghidra.program.database.IntRangeMap;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
@ -376,6 +377,16 @@ public class ProgramTestDouble implements Program {
throw new UnsupportedOperationException();
}
@Override
public CategoryPath getPreferredRootNamespaceCategoryPath() {
return null;
}
@Override
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
throw new UnsupportedOperationException();
}
@Override
public String getExecutablePath() {
throw new UnsupportedOperationException();