mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-01-31 21:41:07 +00:00
GP-1994 refined datatype search and add ability to specify a program's
preferred root-namespace category node
This commit is contained in:
parent
4b600847eb
commit
d7fc209657
@ -46,7 +46,8 @@ import ghidra.program.util.ChangeManager;
|
||||
import ghidra.program.util.ProgramChangeRecord;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.listing.*;
|
||||
import ghidra.trace.database.memory.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.database.symbol.DBTraceFunctionSymbolView;
|
||||
import ghidra.trace.model.Trace.*;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
@ -1071,6 +1072,18 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CategoryPath getPreferredRootNamespaceCategoryPath() {
|
||||
// TODO: not yet implemented
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
|
||||
// TODO: not yet implemented
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecutablePath() {
|
||||
return trace.getExecutablePath();
|
||||
|
@ -26,6 +26,7 @@ import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.IntRangeMap;
|
||||
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.pcode.Varnode;
|
||||
@ -141,6 +142,16 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
||||
view.setCompiler(compiler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CategoryPath getPreferredRootNamespaceCategoryPath() {
|
||||
return view.getPreferredRootNamespaceCategoryPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
|
||||
view.setPreferredRootNamespaceCategoryPath(categoryPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecutablePath() {
|
||||
return view.getExecutablePath();
|
||||
|
@ -342,6 +342,9 @@ public class DemangledDataType extends DemangledType {
|
||||
static DataType findDataType(DataTypeManager dataTypeManager, Demangled namespace,
|
||||
String dtName) {
|
||||
|
||||
// TODO: add support for use of Program.getPreferredRootNamespaceCategoryPath when
|
||||
// searching for datatypes
|
||||
|
||||
List<DataType> list = new ArrayList<>();
|
||||
dataTypeManager.findDataTypes(dtName, list);
|
||||
if (list.isEmpty()) {
|
||||
|
@ -0,0 +1,201 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.data;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.GhidraClass;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
||||
public class DataTypeUtilitiesFindTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private ProgramDB program;
|
||||
private DataTypeManagerDB dataMgr;
|
||||
private SymbolTable symTab;
|
||||
|
||||
private GhidraClass a;
|
||||
private Namespace ab;
|
||||
private GhidraClass aba;
|
||||
private Namespace abab;
|
||||
private GhidraClass ababa;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
|
||||
dataMgr = program.getDataTypeManager();
|
||||
symTab = program.getSymbolTable();
|
||||
program.startTransaction("Test");
|
||||
|
||||
a = symTab.createClass(program.getGlobalNamespace(), "A", SourceType.USER_DEFINED);
|
||||
ab = symTab.createNameSpace(a, "B", SourceType.USER_DEFINED); // A::B
|
||||
aba = symTab.createClass(ab, "A", SourceType.USER_DEFINED); // A::B::A
|
||||
abab = symTab.createNameSpace(aba, "B", SourceType.USER_DEFINED); // A::B::A::B
|
||||
ababa = symTab.createClass(abab, "A", SourceType.USER_DEFINED); // A::B::A::B::A
|
||||
|
||||
StructureDataType structA = new StructureDataType("A", 0);
|
||||
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
CategoryPath cp = new CategoryPath("/x/A");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/x/A/B");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/x/A/B/A");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/x/A/B/A/B");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/x/A/B/A/B/A");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/y/A");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/y/A/B");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/y/A/B/A");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
cp = new CategoryPath("/y/A/B/A/B");
|
||||
structA.setCategoryPath(cp);
|
||||
// omit struct fro category
|
||||
|
||||
cp = new CategoryPath("/y/A/B/A/B/A");
|
||||
structA.setCategoryPath(cp);
|
||||
dataMgr.resolve(structA, null);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.release(this);
|
||||
}
|
||||
|
||||
private void assertPath(DataType dt, String path) {
|
||||
assertNotNull(dt);
|
||||
assertEquals(path, dt.getPathName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDataType() {
|
||||
|
||||
DataType dt = DataTypeUtilities.findDataType(dataMgr, program.getGlobalNamespace(), "A",
|
||||
Structure.class);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, null, "A", null);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, ab, "A", Structure.class);
|
||||
assertPath(dt, "/x/A/B/A");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, aba, "A", Structure.class);
|
||||
assertPath(dt, "/x/A/B/A/A");
|
||||
|
||||
program.setPreferredRootNamespaceCategoryPath("/y");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, program.getGlobalNamespace(), "A",
|
||||
Structure.class);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, null, "A", null);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, ab, "A", Structure.class);
|
||||
assertPath(dt, "/y/A/B/A");
|
||||
|
||||
dt = DataTypeUtilities.findDataType(dataMgr, aba, "A", Structure.class);
|
||||
assertPath(dt, "/y/A/B/A/A");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findExistingClassStruct() {
|
||||
|
||||
// NOTE: search gives preference to class structure found in parent-namespace
|
||||
|
||||
DataType dt = DataTypeUtilities.findExistingClassStruct(dataMgr, a);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, aba); // A::B::A
|
||||
assertPath(dt, "/x/A/B/A");
|
||||
|
||||
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, ababa); // A::B::A::B::A
|
||||
assertPath(dt, "/x/A/B/A/B/A");
|
||||
|
||||
program.setPreferredRootNamespaceCategoryPath("/y");
|
||||
|
||||
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, a);
|
||||
assertPath(dt, "/y/A/A"); // not found in parent /y
|
||||
|
||||
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, aba); // A::B::A
|
||||
assertPath(dt, "/y/A/B/A");
|
||||
|
||||
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, ababa); // A::B::A::B::A
|
||||
assertPath(dt, "/y/A/B/A/B/A/A"); // not found in parent /y/A/B/A/B
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNamespaceQualifiedDataType() {
|
||||
|
||||
DataType dt =
|
||||
DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", Structure.class);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", null);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::A", Structure.class);
|
||||
assertPath(dt, "/x/A/A");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::B::A", Structure.class);
|
||||
assertPath(dt, "/x/A/B/A");
|
||||
|
||||
program.setPreferredRootNamespaceCategoryPath("/y");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", Structure.class);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", null);
|
||||
assertPath(dt, "/A");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::A", Structure.class);
|
||||
assertPath(dt, "/y/A/A");
|
||||
|
||||
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::B::A", Structure.class);
|
||||
assertPath(dt, "/y/A/B/A");
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -60,8 +60,12 @@ public abstract class DomainObjectAdapter implements DomainObject {
|
||||
private ArrayList<Object> consumers;
|
||||
protected Map<String, String> metadata = new LinkedHashMap<String, String>();
|
||||
|
||||
// A flag indicating whether the domain object has changed. Any methods of this domain object
|
||||
// which cause its state to change must set this flag to true
|
||||
// FIXME: (see GP-2003) "changed" flag is improperly manipulated by various methods.
|
||||
// In general, comitted transactions will trigger all valid cases of setting flag to true,
|
||||
// there may be a few cases where setting it to false may be appropriate. Without a transation
|
||||
// it's unclear why it should ever need to get set true.
|
||||
|
||||
// A flag indicating whether the domain object has changed.
|
||||
protected boolean changed = false;
|
||||
|
||||
// a flag indicating that this object is temporary
|
||||
|
@ -214,9 +214,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see db.util.ErrorHandler#dbError(java.io.IOException)
|
||||
*/
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
fatalErrorOccurred = true;
|
||||
@ -238,9 +235,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#getOptions(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Options getOptions(String propertyListName) {
|
||||
return new SubOptions(options, propertyListName, propertyListName + Options.DELIMITER);
|
||||
@ -259,25 +253,16 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
options.performAlterations(propertyAlterations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#canLock()
|
||||
*/
|
||||
@Override
|
||||
public boolean canLock() {
|
||||
return transactionMgr.getCurrentTransaction() == null && !closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#isLocked()
|
||||
*/
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
return transactionMgr.isLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#lock(String)
|
||||
*/
|
||||
@Override
|
||||
public boolean lock(String reason) {
|
||||
return transactionMgr.lock(reason);
|
||||
@ -308,17 +293,11 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return transactionMgr.lockForSnapshot(this, hasProgress, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#forceLock(boolean, String)
|
||||
*/
|
||||
@Override
|
||||
public void forceLock(boolean rollback, String reason) {
|
||||
transactionMgr.forceLock(rollback, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#unlock()
|
||||
*/
|
||||
@Override
|
||||
public void unlock() {
|
||||
transactionMgr.unlock();
|
||||
@ -336,9 +315,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return startTransaction(description, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.UndoableDomainObject#startTransaction(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public int startTransaction(String description, AbortedTransactionListener listener) {
|
||||
int id = -1;
|
||||
@ -359,9 +335,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.UndoableDomainObject#endTransaction(int, boolean)
|
||||
*/
|
||||
@Override
|
||||
public void endTransaction(int transactionID, boolean commit) {
|
||||
transactionMgr.endTransaction(this, transactionID, commit, true);
|
||||
@ -395,65 +368,41 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return transactionMgr.getUndoStackDepth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#canRedo()
|
||||
*/
|
||||
@Override
|
||||
public boolean canRedo() {
|
||||
return transactionMgr.canRedo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#canUndo()
|
||||
*/
|
||||
@Override
|
||||
public boolean canUndo() {
|
||||
return transactionMgr.canUndo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#getRedoName()
|
||||
*/
|
||||
@Override
|
||||
public String getRedoName() {
|
||||
return transactionMgr.getRedoName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#getUndoName()
|
||||
*/
|
||||
@Override
|
||||
public String getUndoName() {
|
||||
return transactionMgr.getUndoName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.UndoableDomainObject#getCurrentTransaction()
|
||||
*/
|
||||
@Override
|
||||
public Transaction getCurrentTransaction() {
|
||||
return transactionMgr.getCurrentTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#redo()
|
||||
*/
|
||||
@Override
|
||||
public void redo() throws IOException {
|
||||
transactionMgr.redo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#undo()
|
||||
*/
|
||||
@Override
|
||||
public void undo() throws IOException {
|
||||
transactionMgr.undo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#isChanged()
|
||||
*/
|
||||
@Override
|
||||
public boolean isChanged() {
|
||||
if (dbh == null) {
|
||||
@ -484,9 +433,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.Undoable#clearUndo()
|
||||
*/
|
||||
@Override
|
||||
public void clearUndo() {
|
||||
clearUndo(true);
|
||||
@ -500,9 +446,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
options.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#canSave()
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean canSave() {
|
||||
DomainFile df = getDomainFile();
|
||||
@ -512,9 +455,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
return dbh.canUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#save(java.lang.String, ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException {
|
||||
if (!canSave()) {
|
||||
@ -554,9 +494,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#saveToPackedFile(java.io.File, ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public void saveToPackedFile(File outputFile, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
@ -620,17 +557,11 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObject#isClosed()
|
||||
*/
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.framework.model.UndoableDomainObject#hasTerminatedTransaction()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasTerminatedTransaction() {
|
||||
return transactionMgr.hasTerminatedTransaction();
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>.
|
||||
|
@ -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 < v2, 0 if equal, and
|
||||
* positive if v1 > 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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user