mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02: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
12 changed files with 734 additions and 179 deletions
|
@ -46,7 +46,8 @@ import ghidra.program.util.ChangeManager;
|
||||||
import ghidra.program.util.ProgramChangeRecord;
|
import ghidra.program.util.ProgramChangeRecord;
|
||||||
import ghidra.trace.database.DBTrace;
|
import ghidra.trace.database.DBTrace;
|
||||||
import ghidra.trace.database.listing.*;
|
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.database.symbol.DBTraceFunctionSymbolView;
|
||||||
import ghidra.trace.model.Trace.*;
|
import ghidra.trace.model.Trace.*;
|
||||||
import ghidra.trace.model.TraceAddressSnapRange;
|
import ghidra.trace.model.TraceAddressSnapRange;
|
||||||
|
@ -1071,6 +1072,18 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||||
throw new UnsupportedOperationException();
|
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
|
@Override
|
||||||
public String getExecutablePath() {
|
public String getExecutablePath() {
|
||||||
return trace.getExecutablePath();
|
return trace.getExecutablePath();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import ghidra.framework.store.LockException;
|
||||||
import ghidra.program.database.IntRangeMap;
|
import ghidra.program.database.IntRangeMap;
|
||||||
import ghidra.program.database.map.AddressMap;
|
import ghidra.program.database.map.AddressMap;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.data.CategoryPath;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
@ -141,6 +142,16 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
||||||
view.setCompiler(compiler);
|
view.setCompiler(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CategoryPath getPreferredRootNamespaceCategoryPath() {
|
||||||
|
return view.getPreferredRootNamespaceCategoryPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
|
||||||
|
view.setPreferredRootNamespaceCategoryPath(categoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getExecutablePath() {
|
public String getExecutablePath() {
|
||||||
return view.getExecutablePath();
|
return view.getExecutablePath();
|
||||||
|
|
|
@ -342,6 +342,9 @@ public class DemangledDataType extends DemangledType {
|
||||||
static DataType findDataType(DataTypeManager dataTypeManager, Demangled namespace,
|
static DataType findDataType(DataTypeManager dataTypeManager, Demangled namespace,
|
||||||
String dtName) {
|
String dtName) {
|
||||||
|
|
||||||
|
// TODO: add support for use of Program.getPreferredRootNamespaceCategoryPath when
|
||||||
|
// searching for datatypes
|
||||||
|
|
||||||
List<DataType> list = new ArrayList<>();
|
List<DataType> list = new ArrayList<>();
|
||||||
dataTypeManager.findDataTypes(dtName, list);
|
dataTypeManager.findDataTypes(dtName, list);
|
||||||
if (list.isEmpty()) {
|
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;
|
private ArrayList<Object> consumers;
|
||||||
protected Map<String, String> metadata = new LinkedHashMap<String, String>();
|
protected Map<String, String> metadata = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
// A flag indicating whether the domain object has changed. Any methods of this domain object
|
// FIXME: (see GP-2003) "changed" flag is improperly manipulated by various methods.
|
||||||
// which cause its state to change must set this flag to true
|
// 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;
|
protected boolean changed = false;
|
||||||
|
|
||||||
// a flag indicating that this object is temporary
|
// a flag indicating that this object is temporary
|
||||||
|
|
|
@ -214,9 +214,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return changeSet;
|
return changeSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see db.util.ErrorHandler#dbError(java.io.IOException)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void dbError(IOException e) {
|
public void dbError(IOException e) {
|
||||||
fatalErrorOccurred = true;
|
fatalErrorOccurred = true;
|
||||||
|
@ -238,9 +235,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#getOptions(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Options getOptions(String propertyListName) {
|
public Options getOptions(String propertyListName) {
|
||||||
return new SubOptions(options, propertyListName, propertyListName + Options.DELIMITER);
|
return new SubOptions(options, propertyListName, propertyListName + Options.DELIMITER);
|
||||||
|
@ -259,25 +253,16 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
options.performAlterations(propertyAlterations);
|
options.performAlterations(propertyAlterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#canLock()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canLock() {
|
public boolean canLock() {
|
||||||
return transactionMgr.getCurrentTransaction() == null && !closed;
|
return transactionMgr.getCurrentTransaction() == null && !closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#isLocked()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLocked() {
|
public boolean isLocked() {
|
||||||
return transactionMgr.isLocked();
|
return transactionMgr.isLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#lock(String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean lock(String reason) {
|
public boolean lock(String reason) {
|
||||||
return transactionMgr.lock(reason);
|
return transactionMgr.lock(reason);
|
||||||
|
@ -308,17 +293,11 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return transactionMgr.lockForSnapshot(this, hasProgress, title);
|
return transactionMgr.lockForSnapshot(this, hasProgress, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#forceLock(boolean, String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void forceLock(boolean rollback, String reason) {
|
public void forceLock(boolean rollback, String reason) {
|
||||||
transactionMgr.forceLock(rollback, reason);
|
transactionMgr.forceLock(rollback, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#unlock()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void unlock() {
|
public void unlock() {
|
||||||
transactionMgr.unlock();
|
transactionMgr.unlock();
|
||||||
|
@ -336,9 +315,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return startTransaction(description, null);
|
return startTransaction(description, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.UndoableDomainObject#startTransaction(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int startTransaction(String description, AbortedTransactionListener listener) {
|
public int startTransaction(String description, AbortedTransactionListener listener) {
|
||||||
int id = -1;
|
int id = -1;
|
||||||
|
@ -359,9 +335,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.UndoableDomainObject#endTransaction(int, boolean)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void endTransaction(int transactionID, boolean commit) {
|
public void endTransaction(int transactionID, boolean commit) {
|
||||||
transactionMgr.endTransaction(this, transactionID, commit, true);
|
transactionMgr.endTransaction(this, transactionID, commit, true);
|
||||||
|
@ -395,65 +368,41 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return transactionMgr.getUndoStackDepth();
|
return transactionMgr.getUndoStackDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#canRedo()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canRedo() {
|
public boolean canRedo() {
|
||||||
return transactionMgr.canRedo();
|
return transactionMgr.canRedo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#canUndo()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canUndo() {
|
public boolean canUndo() {
|
||||||
return transactionMgr.canUndo();
|
return transactionMgr.canUndo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#getRedoName()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getRedoName() {
|
public String getRedoName() {
|
||||||
return transactionMgr.getRedoName();
|
return transactionMgr.getRedoName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#getUndoName()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getUndoName() {
|
public String getUndoName() {
|
||||||
return transactionMgr.getUndoName();
|
return transactionMgr.getUndoName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.UndoableDomainObject#getCurrentTransaction()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Transaction getCurrentTransaction() {
|
public Transaction getCurrentTransaction() {
|
||||||
return transactionMgr.getCurrentTransaction();
|
return transactionMgr.getCurrentTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#redo()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void redo() throws IOException {
|
public void redo() throws IOException {
|
||||||
transactionMgr.redo();
|
transactionMgr.redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#undo()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void undo() throws IOException {
|
public void undo() throws IOException {
|
||||||
transactionMgr.undo();
|
transactionMgr.undo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#isChanged()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChanged() {
|
public boolean isChanged() {
|
||||||
if (dbh == null) {
|
if (dbh == null) {
|
||||||
|
@ -484,9 +433,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.Undoable#clearUndo()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void clearUndo() {
|
public void clearUndo() {
|
||||||
clearUndo(true);
|
clearUndo(true);
|
||||||
|
@ -500,9 +446,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
options.clearCache();
|
options.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#canSave()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean canSave() {
|
public synchronized boolean canSave() {
|
||||||
DomainFile df = getDomainFile();
|
DomainFile df = getDomainFile();
|
||||||
|
@ -512,9 +455,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
return dbh.canUpdate();
|
return dbh.canUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#save(java.lang.String, ghidra.util.task.TaskMonitor)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException {
|
public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
if (!canSave()) {
|
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
|
@Override
|
||||||
public void saveToPackedFile(File outputFile, TaskMonitor monitor)
|
public void saveToPackedFile(File outputFile, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
|
@ -620,17 +557,11 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.DomainObject#isClosed()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return closed;
|
return closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.framework.model.UndoableDomainObject#hasTerminatedTransaction()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasTerminatedTransaction() {
|
public boolean hasTerminatedTransaction() {
|
||||||
return transactionMgr.hasTerminatedTransaction();
|
return transactionMgr.hasTerminatedTransaction();
|
||||||
|
|
|
@ -19,6 +19,8 @@ import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import db.DBConstants;
|
import db.DBConstants;
|
||||||
import db.DBHandle;
|
import db.DBHandle;
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
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.symbol.*;
|
||||||
import ghidra.program.database.util.AddressSetPropertyMapDB;
|
import ghidra.program.database.util.AddressSetPropertyMapDB;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.data.CategoryPath;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
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, AddressSetPropertyMapDB> addrSetPropertyMap = new HashMap<>();
|
||||||
private Map<String, IntRangeMapDB> intRangePropertyMap = 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
|
* Constructs a new ProgramDB
|
||||||
* @param name the name of the program
|
* @param name the name of the program
|
||||||
|
@ -247,7 +255,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
listing = new ListingDB();
|
listing = new ListingDB();
|
||||||
changeSet = new ProgramDBChangeSet(addrMap, NUM_UNDOS);
|
changeSet = new ProgramDBChangeSet(addrMap, NUM_UNDOS);
|
||||||
initManagers(CREATE, TaskMonitor.DUMMY);
|
initManagers(CREATE, TaskMonitor.DUMMY);
|
||||||
propertiesCreate();
|
createProgramInformationOptions();
|
||||||
programUserData = new ProgramUserDataDB(this);
|
programUserData = new ProgramUserDataDB(this);
|
||||||
endTransaction(id, true);
|
endTransaction(id, true);
|
||||||
clearUndo(false);
|
clearUndo(false);
|
||||||
|
@ -369,7 +377,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertiesRestore();
|
registerProgramInformationOptions();
|
||||||
|
restoreProgramInformationOptions();
|
||||||
recordChanges = true;
|
recordChanges = true;
|
||||||
endTransaction(id, true);
|
endTransaction(id, true);
|
||||||
clearUndo(false);
|
clearUndo(false);
|
||||||
|
@ -433,25 +442,59 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
recordChanges = true;
|
recordChanges = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propertiesRestore() {
|
private void registerProgramInformationOptions() {
|
||||||
Options pl = getOptions(PROGRAM_INFO);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
boolean origChangeState = changed;
|
|
||||||
pl.registerOption(EXECUTABLE_PATH, UNKNOWN, null, "Original import path of program image");
|
pl.registerOption(EXECUTABLE_PATH, UNKNOWN, null, "Original import path of program image");
|
||||||
pl.registerOption(EXECUTABLE_FORMAT, UNKNOWN, null, "Original program image format");
|
pl.registerOption(EXECUTABLE_FORMAT, UNKNOWN, null, "Original program image format");
|
||||||
pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "3.0 or earlier", null,
|
pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "3.0 or earlier", null,
|
||||||
"Version of Ghidra used to create this program.");
|
"Version of Ghidra used to create this program.");
|
||||||
pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created");
|
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);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
boolean origChangeState = changed;
|
|
||||||
pl.setString(EXECUTABLE_PATH, UNKNOWN);
|
pl.setString(EXECUTABLE_PATH, UNKNOWN);
|
||||||
pl.setString(EXECUTABLE_FORMAT, UNKNOWN);
|
pl.setString(EXECUTABLE_FORMAT, UNKNOWN);
|
||||||
pl.setString(CREATED_WITH_GHIDRA_VERSION, Application.getApplicationVersion());
|
pl.setString(CREATED_WITH_GHIDRA_VERSION, Application.getApplicationVersion());
|
||||||
pl.setDate(DATE_CREATED, new Date());
|
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) {
|
void setProgramUserData(ProgramUserDataDB programUserData) {
|
||||||
|
@ -556,7 +599,17 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
public void setCompiler(String compiler) {
|
public void setCompiler(String compiler) {
|
||||||
Options pl = getOptions(PROGRAM_INFO);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
pl.setString(COMPILER, compiler);
|
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
|
@Override
|
||||||
|
@ -571,7 +624,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
public void setExecutablePath(String path) {
|
public void setExecutablePath(String path) {
|
||||||
Options pl = getOptions(PROGRAM_INFO);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
pl.setString(EXECUTABLE_PATH, path);
|
pl.setString(EXECUTABLE_PATH, path);
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -591,7 +643,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
public void setExecutableFormat(String format) {
|
public void setExecutableFormat(String format) {
|
||||||
Options pl = getOptions(PROGRAM_INFO);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
pl.setString(EXECUTABLE_FORMAT, format);
|
pl.setString(EXECUTABLE_FORMAT, format);
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -611,7 +662,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
public void setExecutableMD5(String md5) {
|
public void setExecutableMD5(String md5) {
|
||||||
Options pl = getOptions(PROGRAM_INFO);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
pl.setString(EXECUTABLE_MD5, md5);
|
pl.setString(EXECUTABLE_MD5, md5);
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -631,7 +681,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
public void setExecutableSHA256(String sha256) {
|
public void setExecutableSHA256(String sha256) {
|
||||||
Options pl = getOptions(PROGRAM_INFO);
|
Options pl = getOptions(PROGRAM_INFO);
|
||||||
pl.setString(EXECUTABLE_SHA256, sha256);
|
pl.setString(EXECUTABLE_SHA256, sha256);
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1738,6 +1787,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
||||||
for (int i = 0; i < NUM_MANAGERS; i++) {
|
for (int i = 0; i < NUM_MANAGERS; i++) {
|
||||||
managers[i].invalidateCache(all);
|
managers[i].invalidateCache(all);
|
||||||
}
|
}
|
||||||
|
restoreProgramInformationOptions();
|
||||||
installExtensions(); // Reload any extensions
|
installExtensions(); // Reload any extensions
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
|
|
@ -19,11 +19,11 @@ import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import ghidra.app.util.NamespaceUtils;
|
import ghidra.app.util.NamespaceUtils;
|
||||||
|
import ghidra.app.util.SymbolPathParser;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.model.address.GlobalNamespace;
|
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
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.program.model.symbol.Namespace;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
@ -354,11 +354,37 @@ public class DataTypeUtilities {
|
||||||
: new CategoryPath(baseCategory, categoryPathParts);
|
: 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
|
* 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
|
* within the specified dataTypeManager. The first match which satisfies the category path
|
||||||
* used in checking data type parent categories, however if no type corresponds to the namespace
|
* requirement will be returned. If a non-root namespace is specified the datatype's trailing
|
||||||
* another type whose name matches may be returned.
|
* category path must match the specified namespace path.
|
||||||
*
|
*
|
||||||
* @param dataTypeManager data type manager
|
* @param dataTypeManager data type manager
|
||||||
* @param namespace namespace associated with dtName (null indicates no namespace constraint)
|
* @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
|
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
|
||||||
* @return best matching data type
|
* @return best matching data type
|
||||||
*/
|
*/
|
||||||
public static DataType findDataType(DataTypeManager dataTypeManager, Namespace namespace,
|
public static <T extends DataType> T findDataType(DataTypeManager dataTypeManager,
|
||||||
String dtName, Class<? extends DataType> classConstraint) {
|
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,
|
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
|
* 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
|
* specified dataTypeManager. The namespace will be used in checking data type parent categories.
|
||||||
* checking data type parent categories, however if no type corresponds to the namespace another
|
* NOTE: name parsing assumes :: namespace delimiter which can be thrown off if name includes
|
||||||
* type whose name matches may be returned. NOTE: name parsing assumes :: delimiter and can be
|
* template information which could contain namespaces (see {@link SymbolPathParser#parse(String)}).
|
||||||
* thrown off if name include template information which could contain namespaces.
|
|
||||||
*
|
*
|
||||||
* @param dataTypeManager data type manager
|
* @param dataTypeManager data type manager
|
||||||
* @param dtNameWithNamespace name of data type qualified with namespace (e.g.,
|
* @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
|
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
|
||||||
* @return best matching data type
|
* @return best matching data type
|
||||||
*/
|
*/
|
||||||
public static DataType findNamespaceQualifiedDataType(DataTypeManager dataTypeManager,
|
public static <T extends DataType> T findNamespaceQualifiedDataType(
|
||||||
String dtNameWithNamespace, Class<? extends DataType> classConstraint) {
|
DataTypeManager dataTypeManager,
|
||||||
|
String dtNameWithNamespace, Class<T> classConstraint) {
|
||||||
|
|
||||||
String[] splitName = dtNameWithNamespace.split(Namespace.DELIMITER);
|
List<String> pathList = SymbolPathParser.parse(dtNameWithNamespace);
|
||||||
String dtName = splitName[splitName.length - 1];
|
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,
|
return findDataType(dataTypeManager, dtName, classConstraint,
|
||||||
dataType -> hasPreferredNamespaceCategory(dataType, splitName));
|
categoryPath -> getCategoryMatchType(categoryPath, namespacePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -410,40 +464,92 @@ public class DataTypeUtilities {
|
||||||
return cPrimitiveNameMap.get(dataTypeName);
|
return cPrimitiveNameMap.get(dataTypeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasPreferredNamespaceCategory(DataType dataType,
|
private static final int NAMESPACE_PATH_INDEX = 0;
|
||||||
String[] splitDataTypeName) {
|
private static final int PARENT_NAMESPACE_PATH_INDEX = 1;
|
||||||
// last element of split array is data type name and is ignored here
|
|
||||||
if (splitDataTypeName.length == 1) {
|
/**
|
||||||
return true;
|
* 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();
|
String[] paths = new String[2];
|
||||||
int index = splitDataTypeName.length - 2;
|
StringBuilder buf = new StringBuilder();
|
||||||
while (index >= 0) {
|
for (String n : namespace.getParentNamespace().getPathList(true)) {
|
||||||
if (categoryPath.equals(CategoryPath.ROOT) ||
|
buf.append(CategoryPath.DELIMITER_STRING);
|
||||||
!categoryPath.getName().equals(splitDataTypeName[index])) {
|
buf.append(n);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
categoryPath = categoryPath.getParent();
|
|
||||||
--index;
|
|
||||||
}
|
}
|
||||||
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) {
|
private enum CategoryMatchType {
|
||||||
if (namespace == null) {
|
NONE, SECONDARY, PREFERRED;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
String path = categoryPath.getPath();
|
||||||
Namespace ns = namespace;
|
return path.endsWith(namespacePath) ? CategoryMatchType.PREFERRED : CategoryMatchType.NONE;
|
||||||
while (!(ns instanceof GlobalNamespace) && !(ns instanceof Library)) {
|
}
|
||||||
if (categoryPath.equals(CategoryPath.ROOT) ||
|
|
||||||
!categoryPath.getName().equals(ns.getName())) {
|
/**
|
||||||
return false;
|
* Namespace category matcher.
|
||||||
}
|
* @param categoryPath datatype category path
|
||||||
categoryPath = categoryPath.getParent();
|
* @param namespacePaths namespace paths constraint or null for no namespace. This value should
|
||||||
ns = ns.getParentNamespace();
|
* 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.
|
* preferred namespace.
|
||||||
*/
|
*/
|
||||||
private static interface NamespaceMatcher {
|
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,
|
private static CategoryPath getPreferredRootNamespaceCategoryPath(
|
||||||
Class<? extends DataType> classConstraint, NamespaceMatcher preferredCategoryMatcher) {
|
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<>();
|
ArrayList<DataType> list = new ArrayList<>();
|
||||||
dataTypeManager.findDataTypes(dtName, list);
|
dataTypeManager.findDataTypes(dtName, list);
|
||||||
|
Collections.sort(list, DATATYPE_CATEGORY_PATH_LENGTH_COMPARATOR);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
//use the datatype that exists in the root category,
|
T secondaryMatch = null;
|
||||||
//otherwise just pick the first one...
|
|
||||||
DataType anyDt = null;
|
|
||||||
DataType preferredDataType = null;
|
|
||||||
for (DataType existingDT : list) {
|
for (DataType existingDT : list) {
|
||||||
if (classConstraint != null &&
|
if (classConstraint != null &&
|
||||||
!classConstraint.isAssignableFrom(existingDT.getClass())) {
|
!classConstraint.isAssignableFrom(existingDT.getClass())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (preferredCategoryMatcher == null) {
|
CategoryMatchType matchType =
|
||||||
if (existingDT.getCategoryPath().equals(CategoryPath.ROOT)) {
|
categoryMatcher.getMatchType(existingDT.getCategoryPath());
|
||||||
return existingDT;
|
if (matchType == CategoryMatchType.PREFERRED) {
|
||||||
}
|
return (T) existingDT; // preferred match
|
||||||
}
|
}
|
||||||
if (preferredCategoryMatcher.isNamespaceCategoryMatch(existingDT)) {
|
else if (secondaryMatch == null && matchType == CategoryMatchType.SECONDARY) {
|
||||||
preferredDataType = existingDT;
|
secondaryMatch = (T) existingDT;
|
||||||
}
|
}
|
||||||
// If all else fails return any matching name for backward compatibility
|
|
||||||
anyDt = existingDT;
|
|
||||||
}
|
}
|
||||||
if (preferredDataType != null) {
|
return secondaryMatch;
|
||||||
return preferredDataType;
|
|
||||||
}
|
|
||||||
return anyDt;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Date;
|
||||||
|
|
||||||
import ghidra.framework.store.LockException;
|
import ghidra.framework.store.LockException;
|
||||||
import ghidra.program.database.IntRangeMap;
|
import ghidra.program.database.IntRangeMap;
|
||||||
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.database.map.AddressMap;
|
import ghidra.program.database.map.AddressMap;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -60,6 +61,9 @@ public interface Program extends DataTypeManagerDomainObject {
|
||||||
public static final String DATE_CREATED = "Date Created";
|
public static final String DATE_CREATED = "Date Created";
|
||||||
/** Name of ghidra version property */
|
/** Name of ghidra version property */
|
||||||
public static final String CREATED_WITH_GHIDRA_VERSION = "Created With Ghidra Version";
|
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 */
|
/** Creation date to ask for analysis */
|
||||||
public static final String ANALYSIS_START_DATE = "2007-Jan-01";
|
public static final String ANALYSIS_START_DATE = "2007-Jan-01";
|
||||||
/** Format string of analysis date */
|
/** Format string of analysis date */
|
||||||
|
@ -160,6 +164,37 @@ public interface Program extends DataTypeManagerDomainObject {
|
||||||
*/
|
*/
|
||||||
public void setCompiler(String compiler);
|
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.
|
* Gets the path to the program's executable file.
|
||||||
* For example, <code>C:\Temp\test.exe</code>.
|
* For example, <code>C:\Temp\test.exe</code>.
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class VariableUtilities {
|
||||||
/**
|
/**
|
||||||
* Get a precedence value for the specified variable.
|
* Get a precedence value for the specified variable.
|
||||||
* This value can be used to assist with LocalVariable.compareTo(Variable var)
|
* This value can be used to assist with LocalVariable.compareTo(Variable var)
|
||||||
* @param var
|
* @param var function variable
|
||||||
* @return numeric precedence
|
* @return numeric precedence
|
||||||
*/
|
*/
|
||||||
public static int getPrecedence(Variable var) {
|
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
|
* 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
|
* 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)
|
* all have an empty varnode list and would be considered a match)
|
||||||
* @param vars
|
* @param vars function variables
|
||||||
* @param otherVars
|
* @param otherVars other function variables
|
||||||
* @return true if the exact sequence of variable storage varnodes matches across two lists of 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) {
|
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
|
* 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
|
* 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)
|
* all have an empty varnode list and would be considered a match)
|
||||||
* @param vars
|
* @param vars function variables
|
||||||
* @param otherVars
|
* @param otherVars other function variables
|
||||||
* @return true if the exact sequence of variable storage varnodes matches across two lists of 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) {
|
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.
|
* Compare two variables without using the instance specific compareTo method.
|
||||||
* @param v1
|
* @param v1 a function variable
|
||||||
* @param v2
|
* @param v2 another function variable
|
||||||
* @return a negative value if v1 < v2, 0 if equal, and
|
* @return a negative value if v1 < v2, 0 if equal, and
|
||||||
* positive if v1 > v2
|
* positive if v1 > v2
|
||||||
*/
|
*/
|
||||||
|
@ -162,8 +162,8 @@ public class VariableUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the appropriate data type for an automatic parameter
|
* Determine the appropriate data type for an automatic parameter
|
||||||
* @param function
|
* @param function function whose auto param datatype is to be determined
|
||||||
* @param returnDataType
|
* @param returnDataType function's return datatype
|
||||||
* @param storage variable storage for an auto-parameter (isAutoStorage should be true)
|
* @param storage variable storage for an auto-parameter (isAutoStorage should be true)
|
||||||
* @return auto-parameter data type
|
* @return auto-parameter data type
|
||||||
*/
|
*/
|
||||||
|
@ -174,7 +174,7 @@ public class VariableUtilities {
|
||||||
if (autoParameterType == AutoParameterType.THIS) {
|
if (autoParameterType == AutoParameterType.THIS) {
|
||||||
DataType classStruct = findOrCreateClassStruct(function);
|
DataType classStruct = findOrCreateClassStruct(function);
|
||||||
if (classStruct == null) {
|
if (classStruct == null) {
|
||||||
classStruct = DataType.VOID;
|
classStruct = VoidDataType.dataType;
|
||||||
}
|
}
|
||||||
return getPointer(function.getProgram(), classStruct, storage.size());
|
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 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 dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
|
||||||
* @param allowSizeMismatch if true size mismatch will be ignore
|
* @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,
|
public static void checkStorage(VariableStorage storage, DataType dataType,
|
||||||
boolean allowSizeMismatch) throws InvalidInputException {
|
boolean allowSizeMismatch) throws InvalidInputException {
|
||||||
|
@ -212,7 +212,7 @@ public class VariableUtilities {
|
||||||
* @param dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
|
* @param dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
|
||||||
* @param allowSizeMismatch if true size mismatch will be ignore
|
* @param allowSizeMismatch if true size mismatch will be ignore
|
||||||
* @return original storage or resized storage with the correct size.
|
* @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,
|
public static VariableStorage checkStorage(Function function, VariableStorage storage,
|
||||||
DataType dataType, boolean allowSizeMismatch) throws InvalidInputException {
|
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
|
* 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
|
* storage if current storage does not have a stack component or if other space constraints
|
||||||
* are exceeded.
|
* are exceeded.
|
||||||
* @param curStorage
|
* @param curStorage current variable storage
|
||||||
* @param dataType
|
* @param dataType variable datatype
|
||||||
* @param alignStack if false no attempt is made to align stack usage for big-endian
|
* @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
|
* @return resize storage
|
||||||
* @throws InvalidInputException if unable to resize storage to specified size.
|
* @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.
|
* 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 var existing function variable or null for new variable
|
||||||
* @param newStorage new/updated variable storage
|
* @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
|
* @throws VariableSizeException if deleteConflictingVariables is false and another variable conflicts
|
||||||
*/
|
*/
|
||||||
public static void checkVariableConflict(Function function, Variable var,
|
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.
|
* Check for variable storage conflict and optionally remove conflicting variables.
|
||||||
* @param existingVariables variables to check (may contain null entries)
|
* @param existingVariables variables to check (may contain null entries)
|
||||||
* @param var
|
* @param var function variable
|
||||||
* @param newStorage
|
* @param conflictHandler variable conflict handler
|
||||||
* @throws VariableSizeException
|
* @param newStorage variable storage
|
||||||
* @throws VariableSizeException if another variable conflicts
|
* @throws VariableSizeException if another variable conflicts
|
||||||
*/
|
*/
|
||||||
public static void checkVariableConflict(List<? extends Variable> existingVariables,
|
public static void checkVariableConflict(List<? extends Variable> existingVariables,
|
||||||
|
@ -685,7 +685,7 @@ public class VariableUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the minimum stack offset for parameters
|
* 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
|
* @return stack parameter offset or null if it could not be determined
|
||||||
*/
|
*/
|
||||||
public static Integer getBaseStackParamOffset(Function function) {
|
public static Integer getBaseStackParamOffset(Function function) {
|
||||||
|
@ -712,7 +712,8 @@ public class VariableUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a suitable 'this' parameter for the specified function
|
* 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'
|
* @return this parameter or null of calling convention is not a 'thiscall'
|
||||||
* or some other error prevents it
|
* or some other error prevents it
|
||||||
* @deprecated should rely on auto-param instead - try not to use this method which may be eliminated
|
* @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
|
* 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 preferred structure will utilize a namespace-based category path, however,
|
||||||
* the match criteria can be fuzzy and relies primarily on the class name.
|
* 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
|
* A properly named class structure must reside within a category whose trailing
|
||||||
* manager.
|
* 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 classNamespace class namespace
|
||||||
* @param dataTypeManager data type manager which should be searched and whose
|
* @param dataTypeManager data type manager which should be searched and whose
|
||||||
* data organization should be used.
|
* 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
|
* 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 preferred structure will utilize a namespace-based category path, however,
|
||||||
* the match criteria can be fuzzy and relies primarily on the class name.
|
* 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
|
* @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
|
* @return new or existing structure whose name matches the function's class namespace or
|
||||||
* null if function not contained within a class namespace.
|
* 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
|
* 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 preferred structure will utilize a namespace-based category path, however,
|
||||||
* the match criteria can be fuzzy and relies primarily on the class name.
|
* 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 classNamespace class namespace
|
||||||
* @param dataTypeManager data type manager which should be searched.
|
* @param dataTypeManager data type manager which should be searched.
|
||||||
* @return existing structure whose name matches the specified class namespace
|
* @return existing structure whose name matches the specified class namespace
|
||||||
|
@ -817,15 +841,19 @@ public class VariableUtilities {
|
||||||
*/
|
*/
|
||||||
public static Structure findExistingClassStruct(GhidraClass classNamespace,
|
public static Structure findExistingClassStruct(GhidraClass classNamespace,
|
||||||
DataTypeManager dataTypeManager) {
|
DataTypeManager dataTypeManager) {
|
||||||
return (Structure) DataTypeUtilities.findDataType(dataTypeManager,
|
return DataTypeUtilities.findExistingClassStruct(dataTypeManager, classNamespace);
|
||||||
classNamespace.getParentNamespace(), classNamespace.getName(), Structure.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the structure data type which corresponds to the specified function's class namespace
|
* Find the structure data type which corresponds to the specified function's class namespace
|
||||||
* within the function's program.
|
* within the function's program.
|
||||||
|
*
|
||||||
* The preferred structure will utilize a namespace-based category path, however,
|
* The preferred structure will utilize a namespace-based category path, however,
|
||||||
* the match criteria can be fuzzy and relies primarily on the class name.
|
* 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.
|
* @param func the function.
|
||||||
* @return existing structure whose name matches the specified function's class namespace
|
* @return existing structure whose name matches the specified function's class namespace
|
||||||
* or null if not found.
|
* or null if not found.
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.symbol;
|
package ghidra.program.model.symbol;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.CircularDependencyException;
|
import ghidra.program.model.listing.CircularDependencyException;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
@ -24,6 +26,7 @@ import ghidra.util.exception.InvalidInputException;
|
||||||
* The Namespace interface
|
* The Namespace interface
|
||||||
*/
|
*/
|
||||||
public interface Namespace {
|
public interface Namespace {
|
||||||
|
|
||||||
static final long GLOBAL_NAMESPACE_ID = 0;
|
static final long GLOBAL_NAMESPACE_ID = 0;
|
||||||
/**
|
/**
|
||||||
* The delimiter that is used to separate namespace nodes in a namespace
|
* The delimiter that is used to separate namespace nodes in a namespace
|
||||||
|
@ -63,6 +66,24 @@ public interface Namespace {
|
||||||
*/
|
*/
|
||||||
public String getName(boolean includeNamespacePath);
|
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
|
||||||
* @return the namespace id
|
* @return the namespace id
|
||||||
|
@ -96,11 +117,20 @@ public interface Namespace {
|
||||||
throws DuplicateNameException, InvalidInputException, CircularDependencyException;
|
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() {
|
public default boolean isGlobal() {
|
||||||
return getID() == GLOBAL_NAMESPACE_ID;
|
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.data.ProgramDataTypeManager;
|
||||||
import ghidra.program.database.map.AddressMap;
|
import ghidra.program.database.map.AddressMap;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.data.CategoryPath;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
|
@ -376,6 +377,16 @@ public class ProgramTestDouble implements Program {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CategoryPath getPreferredRootNamespaceCategoryPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getExecutablePath() {
|
public String getExecutablePath() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue