diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java index b6585a3794..9b3f916907 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java @@ -264,9 +264,9 @@ class SymPcodeExecutor extends PcodeExecutor { // TODO: Does the decompiler communicate the inferred calling convention? try { PrototypeModel convention = program.getCompilerSpec().findBestCallingConvention(params); - sig.setGenericCallingConvention(convention.getGenericCallingConvention()); + sig.setCallingConvention(convention.getName()); } - catch (SleighException e) { + catch (SleighException | InvalidInputException e) { // Whatever, just leave sig at "unknown" } return sig; @@ -352,7 +352,7 @@ class SymPcodeExecutor extends PcodeExecutor { throw new PcodeExecutionException("Cannot get stack change for indirect call: " + op); } PrototypeModel convention = - program.getCompilerSpec().matchConvention(sig.getGenericCallingConvention()); + program.getCompilerSpec().matchConvention(sig.getCallingConventionName()); if (convention == null) { warnings.add(new UnspecifiedConventionStackUnwindWarning(null)); convention = program.getCompilerSpec().getDefaultCallingConvention(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java index fc96d2e0c2..c2bd9b7db7 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataTypeManager.java @@ -24,7 +24,9 @@ import db.DBHandle; import ghidra.framework.model.DomainFile; import ghidra.program.database.data.ProgramBasedDataTypeManagerDB; import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFactory; import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTraceManager; import ghidra.trace.model.data.TraceBasedDataTypeManager; @@ -38,15 +40,36 @@ import ghidra.util.task.TaskMonitor; public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB implements TraceBasedDataTypeManager, DBTraceManager { - protected final ReadWriteLock lock; + protected final ReadWriteLock lock; // TODO: This lock object is not used protected final DBTrace trace; + private static final String INSTANCE_TABLE_PREFIX = null; // placeholder only + public DBTraceDataTypeManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, TaskMonitor monitor, DBTrace trace) throws CancelledException, VersionException, IOException { - super(dbh, null, openMode.toInteger(), trace, trace.getLock(), monitor); + super(dbh, null, openMode.toInteger(), INSTANCE_TABLE_PREFIX, trace, trace.getLock(), + monitor); this.lock = lock; // TODO: nothing uses this local lock - not sure what its purpose is this.trace = trace; + + setProgramArchitecture(new ProgramArchitecture() { + + @Override + public Language getLanguage() { + return trace.getBaseLanguage(); + } + + @Override + public CompilerSpec getCompilerSpec() { + return trace.getBaseCompilerSpec(); + } + + @Override + public AddressFactory getAddressFactory() { + return trace.getBaseAddressFactory(); + } + }, null, false, monitor); } @Override @@ -233,12 +256,4 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB */ return ArchiveType.PROGRAM; } - - @Override - public DataOrganization getDataOrganization() { - if (dataOrganization == null) { - dataOrganization = trace.getBaseCompilerSpec().getDataOrganization(); - } - return dataOrganization; - } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java index 6b4ed7fd84..8381a53d49 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManager.java @@ -15,11 +15,11 @@ */ package ghidra.trace.database.program; -import static ghidra.lifecycle.Unfinished.TODO; +import static ghidra.lifecycle.Unfinished.*; import java.io.IOException; +import java.util.Collection; import java.util.Iterator; -import java.util.List; import generic.NestedIterator; import ghidra.program.database.ProgramDB; @@ -64,7 +64,7 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager { } @Override - public List getCallingConventionNames() { + public Collection getCallingConventionNames() { return functions.getCallingConventionNames(); } @@ -78,11 +78,6 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager { return functions.getCallingConvention(name); } - @Override - public PrototypeModel[] getCallingConventions() { - return functions.getCallingConventions(); - } - @Override public TraceFunctionSymbol createFunction(String name, Address entryPoint, AddressSetView body, SourceType source) throws InvalidInputException, OverlappingFunctionException { diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbol.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbol.java index 0da88f305d..b3678a2333 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbol.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbol.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import db.DBRecord; +import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.*; import ghidra.program.model.data.*; @@ -33,6 +34,7 @@ import ghidra.program.model.symbol.*; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.AddressDBFieldCodec; import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses; import ghidra.trace.database.bookmark.DBTraceBookmarkType; +import ghidra.trace.database.data.DBTraceDataTypeManager; import ghidra.trace.database.listing.DBTraceCommentAdapter; import ghidra.trace.database.listing.DBTraceData; import ghidra.trace.database.program.DBTraceProgramView; @@ -122,7 +124,7 @@ public class DBTraceFunctionSymbol extends DBTraceNamespaceSymbol @DBAnnotatedField(column = FIXUP_COLUMN_NAME) protected String callFixup; @DBAnnotatedField(column = CALLING_CONVENTION_COLUMN_NAME) - protected byte callingConventionID = DBTraceSymbolManager.DEFAULT_CALLING_CONVENTION_ID; + protected byte callingConventionID = DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID; // TODO: Pack into flags if more bits needed @DBAnnotatedField(column = SIGNATURE_SOURCE_COLUMN_NAME) protected SourceType signatureSource = SourceType.ANALYSIS; // Assumed default, 0-ordinal @@ -484,7 +486,8 @@ public class DBTraceFunctionSymbol extends DBTraceNamespaceSymbol } protected boolean hasExplicitCallingConvention() { - return callingConventionID != -1 && callingConventionID != -2; + return callingConventionID != DataTypeManagerDB.DEFAULT_CALLING_CONVENTION_ID && + callingConventionID != DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID; } @Override @@ -1734,16 +1737,14 @@ public class DBTraceFunctionSymbol extends DBTraceNamespaceSymbol if (cs == null) { return null; } - if (DBTraceSymbolManager.UNKNOWN_CALLING_CONVENTION_ID == callingConventionID) { + DBTraceDataTypeManager dtm = manager.dataTypeManager; + if (callingConventionID == DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID) { return null; } - if (DBTraceSymbolManager.DEFAULT_CALLING_CONVENTION_ID == callingConventionID) { + if (callingConventionID == DataTypeManagerDB.DEFAULT_CALLING_CONVENTION_ID) { return cs.getDefaultCallingConvention(); } - String ccName = manager.callingConventionMap.getKey(callingConventionID); - if (ccName == null) { - return null; - } + String ccName = dtm.getCallingConventionName(callingConventionID); return cs.getCallingConvention(ccName); } } @@ -1751,28 +1752,8 @@ public class DBTraceFunctionSymbol extends DBTraceNamespaceSymbol @Override public String getCallingConventionName() { try (LockHold hold = LockHold.lock(manager.lock.readLock())) { - if (DBTraceSymbolManager.UNKNOWN_CALLING_CONVENTION_ID == callingConventionID) { - return null; - } - if (DBTraceSymbolManager.DEFAULT_CALLING_CONVENTION_ID == callingConventionID) { - return DBTraceSymbolManager.DEFAULT_CALLING_CONVENTION_NAME; - } - return manager.callingConventionMap.getKey(callingConventionID); - } - } - - @Override - public String getDefaultCallingConventionName() { - try (LockHold hold = LockHold.lock(manager.lock.readLock())) { - PrototypeModel cc = manager.functions.getDefaultCallingConvention(); - if (cc == null) { - return DBTraceSymbolManager.DEFAULT_CALLING_CONVENTION_NAME; - } - String ccName = cc.getName(); - if (ccName == null) { // Really? - return DBTraceSymbolManager.DEFAULT_CALLING_CONVENTION_NAME; - } - return ccName; + DBTraceDataTypeManager dtm = manager.dataTypeManager; + return dtm.getCallingConventionName(callingConventionID); } } @@ -1783,12 +1764,16 @@ public class DBTraceFunctionSymbol extends DBTraceNamespaceSymbol thunked.setCallingConvention(name); return; } - if (Objects.equals(getCallingConventionName(), name)) { - return; + + DBTraceDataTypeManager dtm = manager.dataTypeManager; + byte id = dtm.getCallingConventionID(name, true); + if (id == callingConventionID) { + return; // no change } + doLoadVariables(); - this.callingConventionID = manager.findOrRecordCallingConvention(name); + callingConventionID = id; update(CALLING_CONVENTION_COLUMN); boolean hasCustomStorage = hasCustomVariableStorage(); @@ -1813,11 +1798,14 @@ public class DBTraceFunctionSymbol extends DBTraceNamespaceSymbol new TraceChangeRecord<>(TraceFunctionChangeType.CHANGED, getSpace(), this)); } } + catch (IOException e) { + manager.dbError(e); + } } protected void createClassStructIfNeeded() { PrototypeModel cc = getCallingConvention(); - if (cc == null || cc.getGenericCallingConvention() != GenericCallingConvention.thiscall) { + if (cc == null || !CompilerSpec.CALLING_CONVENTION_thiscall.equals(cc.getName())) { return; } Namespace parentNS = getParentNamespace(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbolView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbolView.java index bbdd19f432..7bda22e323 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbolView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceFunctionSymbolView.java @@ -15,8 +15,7 @@ */ package ghidra.trace.database.symbol; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.database.symbol.OverlappingNamespaceException; @@ -165,54 +164,19 @@ public class DBTraceFunctionSymbolView } } - public static List getCallingConventionNames(CompilerSpec cs) { - PrototypeModel[] namedCCs = cs.getCallingConventions(); - List names = new ArrayList<>(2 + namedCCs.length); - names.add(Function.UNKNOWN_CALLING_CONVENTION_STRING); - names.add(Function.DEFAULT_CALLING_CONVENTION_STRING); - for (PrototypeModel model : namedCCs) { - names.add(model.getName()); - } - return names; - } - @Override - public List getCallingConventionNames() { - // TODO: Allow for user-selected compiler spec(s) - return getCallingConventionNames(manager.trace.getBaseCompilerSpec()); + public Collection getCallingConventionNames() { + return manager.dataTypeManager.getDefinedCallingConventionNames(); } @Override public PrototypeModel getDefaultCallingConvention() { - CompilerSpec cs = manager.trace.getBaseCompilerSpec(); - if (cs == null) { - return null; - } - return cs.getDefaultCallingConvention(); + return manager.dataTypeManager.getDefaultCallingConvention(); } @Override public PrototypeModel getCallingConvention(String name) { - CompilerSpec cs = manager.trace.getBaseCompilerSpec(); - if (cs == null) { - return null; - } - if (Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(name)) { - return null; - } - if (Function.DEFAULT_CALLING_CONVENTION_STRING.equals(name)) { - return cs.getDefaultCallingConvention(); - } - return cs.getCallingConvention(name); - } - - @Override - public PrototypeModel[] getCallingConventions() { - CompilerSpec cs = manager.trace.getBaseCompilerSpec(); - if (cs == null) { - return EMPTY_MODEL_LIST; - } - return cs.getCallingConventions(); + return manager.dataTypeManager.getCallingConvention(name); } // TODO: Move this into a FunctionUtilities class? diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolManager.java index d3cd85d3b4..ff43266523 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolManager.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceSymbolManager.java @@ -20,9 +20,6 @@ import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.locks.ReadWriteLock; -import org.apache.commons.collections4.BidiMap; -import org.apache.commons.collections4.bidimap.DualHashBidiMap; - import db.*; import ghidra.program.model.address.*; import ghidra.program.model.data.DataType; @@ -60,11 +57,6 @@ import ghidra.util.task.TaskMonitor; * TODO: See if CALL-type references produce dynamic labels or functions. */ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager { - protected static final byte DEFAULT_CALLING_CONVENTION_ID = -1; - protected static final byte UNKNOWN_CALLING_CONVENTION_ID = -2; - - protected static final String DEFAULT_CALLING_CONVENTION_NAME = "default"; - protected static final String UNKNOWN_CALLING_CONVENTION_NAME = "unknown"; private static final long TYPE_MASK = 0xFF; private static final int TYPE_SHIFT = 64 - 8; @@ -101,32 +93,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager } } - @DBAnnotatedObjectInfo(version = 0) - public static class DBTraceCallingConventionEntry extends DBAnnotatedObject { - static final String TABLE_NAME = "CallingConventions"; - - static final String NAME_COLUMN_NAME = "Name"; - - @DBAnnotatedColumn(NAME_COLUMN_NAME) - static DBObjectColumn NAME_COLUMN; - - @DBAnnotatedField(column = NAME_COLUMN_NAME) - String name; - - public DBTraceCallingConventionEntry(DBCachedObjectStore store, DBRecord record) { - super(store, record); - } - - public void setName(String name) { - this.name = name; - update(NAME_COLUMN); - } - - public String getName() { - return name; - } - } - @DBAnnotatedObjectInfo(version = 0) public static class DBTraceFunctionTag extends DBAnnotatedObject implements FunctionTag { @@ -435,9 +401,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager protected final DBTraceAddressSnapRangePropertyMap idMap; - protected final DBCachedObjectStore callingConventionStore; - protected final BidiMap callingConventionMap = new DualHashBidiMap<>(); - protected final DBCachedObjectStore tagStore; protected final DBCachedObjectIndex tagsByName; @@ -494,11 +457,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager baseLanguage, trace, threadManager, DBTraceSymbolIDEntry.class, DBTraceSymbolIDEntry::new); - callingConventionStore = - factory.getOrCreateCachedStore(DBTraceCallingConventionEntry.TABLE_NAME, - DBTraceCallingConventionEntry.class, DBTraceCallingConventionEntry::new, true); - loadCallingConventions(); - tagStore = factory.getOrCreateCachedStore(DBTraceFunctionTag.TABLE_NAME, DBTraceFunctionTag.class, (s, r) -> new DBTraceFunctionTag(this, s, r), true); tagsByName = tagStore.getIndex(String.class, DBTraceFunctionTag.NAME_COLUMN); @@ -570,9 +528,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager if (ptrSize != dataTypeManager.getDataOrganization().getPointerSize()) { return dataTypeManager.getPointer(formal, ptrSize); } - else { - return dataTypeManager.getPointer(formal); - } + return dataTypeManager.getPointer(formal); } protected > T putInMap(T view) { @@ -608,25 +564,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager return (symbolID >> KEY_SHIFT) & KEY_MASK; } - protected void loadCallingConventions() { - // NOTE: Should already own write lock - for (DBTraceCallingConventionEntry ent : callingConventionStore.asMap().values()) { - // NOTE: No need to check. Only called on new or invalidate. - callingConventionMap.put(ent.name, (byte) ent.getKey()); - } - } - - protected byte doRecordCallingConvention(String name) { - DBTraceCallingConventionEntry ent = callingConventionStore.create(); - ent.setName(name); - return (byte) ent.getKey(); - } - - protected byte findOrRecordCallingConvention(String name) { - // NOTE: Must already have write lock - return callingConventionMap.computeIfAbsent(name, this::doRecordCallingConvention); - } - protected int findOrRecordVariableStorage(VariableStorage storage) { DBTraceVariableStorageEntry entry = storageByStorage.getOne(storage); if (entry == null) { @@ -645,9 +582,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager public void invalidateCache(boolean all) { try (LockHold hold = LockHold.lock(lock.writeLock())) { idMap.invalidateCache(all); - callingConventionStore.invalidateCache(); - callingConventionMap.clear(); - loadCallingConventions(); for (AbstractDBTraceSymbolSingleTypeView view : symbolViews.values()) { view.invalidateCache(); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceFunctionSymbolView.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceFunctionSymbolView.java index 8137ea34d2..5fd3000820 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceFunctionSymbolView.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/symbol/TraceFunctionSymbolView.java @@ -15,7 +15,7 @@ */ package ghidra.trace.model.symbol; -import java.util.List; +import java.util.Collection; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.Address; @@ -37,11 +37,30 @@ public interface TraceFunctionSymbolView extends TraceSymbolWithLocationView getCallingConventionNames(); + /** + * Get the ordered unmodifiable set of defined calling convention names. The reserved names + * "unknown" and "default" are not included. The returned collection may not include all names + * referenced by various functions and function-definitions. This set is limited to + * those defined by the associated compiler specification. + * + * @return the set of defined calling convention names. + */ + Collection getCallingConventionNames(); + /** + * Get the default calling convention's prototype model. + * + * @return the default calling convention prototype model. + */ PrototypeModel getDefaultCallingConvention(); + /** + * Get the prototype model of the calling convention with the specified name from the + * associated compiler specification. If {@link Function#DEFAULT_CALLING_CONVENTION_STRING} + * is specified {@link #getDefaultCallingConvention()} will be returned. + * + * @param name the calling convention name + * @return the named function calling convention prototype model or null if not defined. + */ PrototypeModel getCallingConvention(String name); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManagerTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManagerTest.java index 5a12f6b8e6..7e1255b2e1 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManagerTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/program/DBTraceProgramViewFunctionManagerTest.java @@ -19,8 +19,8 @@ import static ghidra.lifecycle.Unfinished.*; import static org.junit.Assert.*; import java.io.IOException; +import java.util.Collection; import java.util.Iterator; -import java.util.List; import org.junit.*; @@ -423,25 +423,12 @@ public class DBTraceProgramViewFunctionManagerTest extends AbstractGhidraHeadles assertEquals(defaultModel, protoModel); } - @Test - public void testGetCallingConventions() throws Exception { - PrototypeModel[] protoModels = functionManager.getCallingConventions(); - assertTrue(protoModels.length >= 1); - } - @Test public void testGetCallingConventionNames() throws Exception { - - List names = functionManager.getCallingConventionNames(); + Collection names = functionManager.getCallingConventionNames(); assertTrue(names.size() >= 1); - for (String name : names) { - if (Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(name)) { - assertNull(functionManager.getCallingConvention(name)); - } - else { - assertNotNull(functionManager.getCallingConvention(name)); - } + assertNotNull(functionManager.getCallingConvention(name)); } } diff --git a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt index b5399bdd0c..b73d2c577e 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt and b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt index 18cfbb8c9c..e89f7ba5af 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt and b/Ghidra/Features/Base/data/typeinfo/generic/generic_clib_64.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt b/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt index 8a73fe1325..cdb34ed6b2 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt and b/Ghidra/Features/Base/data/typeinfo/mac_10.9/mac_osx.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt index c34d8e0b13..fee722402f 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt and b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_32.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt index 5ed3b70894..2f3299bd85 100644 Binary files a/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt and b/Ghidra/Features/Base/data/typeinfo/win32/windows_vs12_64.gdt differ diff --git a/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java b/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java index 34ce0a23ff..55a2fb4e56 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java +++ b/Ghidra/Features/Base/ghidra_scripts/CompareGDTs.java @@ -26,6 +26,7 @@ import java.util.Iterator; import ghidra.app.script.GhidraScript; import ghidra.program.model.data.*; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; import ghidra.util.UniversalID; public class CompareGDTs extends GhidraScript { @@ -54,13 +55,28 @@ public class CompareGDTs extends GhidraScript { return; } } + + firstArchive = openDataTypeArchive(firstFile, false); + if (firstArchive.getWarning() != ArchiveWarning.NONE) { + popup( + "An architecture language error occured while opening archive (see log for details)\n" + + firstFile.getPath()); + return; + } + + secondArchive = openDataTypeArchive(secondFile, false); + if (secondArchive.getWarning() != ArchiveWarning.NONE) { + popup( + "An architecture language error occured while opening archive (see log for details)\n" + + secondFile.getPath()); + return; + } + matchByName = askYesNo("Match Data Types By Path Name?", "Do you want to match data types by their path names (rather than by Universal ID)?"); checkPointers = askYesNo("Check Pointers?", "Do you want to check Pointers?"); checkArrays = askYesNo("Check Arrays?", "Do you want to check Arrays?"); - firstArchive = FileDataTypeManager.openFileArchive(firstFile, false); - secondArchive = FileDataTypeManager.openFileArchive(secondFile, false); printWriter = new PrintWriter(outputFile); try { compareDataTypes(); diff --git a/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java b/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java index f060066835..a69867fcc7 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FixupCompositeDataTypesScript.java @@ -29,6 +29,7 @@ import ghidra.app.script.GhidraScript; import ghidra.app.services.DataTypeManagerService; import ghidra.framework.model.DomainFile; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.ProjectDataTypeManager; import ghidra.program.database.data.*; import ghidra.program.model.data.BuiltInDataTypeManager; import ghidra.program.model.data.DataTypeManager; diff --git a/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java b/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java index 368a87344f..4df0b93831 100644 --- a/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java +++ b/Ghidra/Features/Base/ghidra_scripts/SynchronizeGDTCategoryPaths.java @@ -24,43 +24,59 @@ import java.io.File; import ghidra.app.script.GhidraScript; import ghidra.program.model.data.Category; import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; public class SynchronizeGDTCategoryPaths extends GhidraScript { - private File firstFile; - private File secondFile; - private FileDataTypeManager firstArchive; - private FileDataTypeManager secondArchive; - @Override protected void run() throws Exception { - firstFile = askFile("Select First GDT File", "Select 1st"); - secondFile = askFile("Select Second GDT File", "Select 2nd"); - try { - firstArchive = FileDataTypeManager.openFileArchive(firstFile, false); - secondArchive = FileDataTypeManager.openFileArchive(secondFile, true); - - int transactionID = secondArchive.startTransaction("Synchronize Category Path Names"); - Category firstCategory = firstArchive.getRootCategory(); - Category secondCategory = secondArchive.getRootCategory(); - - synchronizeCategory(firstCategory, secondCategory); - secondArchive.endTransaction(transactionID, true); - } - finally { - if (firstArchive != null) { - firstArchive.close(); + File firstFile = askFile("Select First GDT File", "Select 1st"); + try (FileDataTypeManager firstArchive = + FileDataTypeManager.openFileArchive(firstFile, false)) { + if (hasWarning(firstArchive, firstFile)) { + return; } - secondArchive.save(); - if (secondArchive != null) { - secondArchive.close(); + + File secondFile = askFile("Select Second GDT File", "Select 2nd"); + try (FileDataTypeManager secondArchive = + FileDataTypeManager.openFileArchive(secondFile, true)) { + if (hasWarning(secondArchive, secondFile)) { + return; + } + + int transactionID = + secondArchive.startTransaction("Synchronize Category Path Names"); + try { + Category firstCategory = firstArchive.getRootCategory(); + Category secondCategory = secondArchive.getRootCategory(); + synchronizeCategory(firstCategory, secondCategory); + } + finally { + secondArchive.endTransaction(transactionID, true); + } } } } + private boolean hasWarning(FileDataTypeManager archive, File file) { + ArchiveWarning warning = archive.getWarning(); + if (warning == ArchiveWarning.NONE) { + return false; + } + if (warning == ArchiveWarning.UPGRADED_LANGUAGE_VERSION) { + return !askYesNo("Archive Upgrade Confirmation", + "A language upgrade has been performed on archive " + file.getName() + + "\nIs it OK to proceed?"); + } + popup( + "An architecture language error occured while opening archive (see log for details)\n" + + file.getPath()); + return true; + } + private void synchronizeCategory(Category firstCategory, Category secondCategory) { Category[] firstCategories = firstCategory.getCategories(); for (Category categoryA : firstCategories) { diff --git a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml index 4c44167d9b..32dd85d8a1 100644 --- a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml @@ -123,18 +123,27 @@ - - - - - - - + sortgroup="e" + text="Ghidra Functionality" + target = "help/topics/Intro/GhidraFunctionality.htm"> + + + + + - + + + + + + + + + + + + @@ -220,16 +229,6 @@ - - - - - - - - - - diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm index 6ac79334c8..4b328fccd8 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm @@ -353,6 +353,14 @@ editing, since they support sharing and version control and therefore allow more than one user to modify them at a time. Only one user at a time can have a file archive opened for editing.

+ +
+

You can assign a specific "Architecture" + to a Data Type Archive while it is open for editing. This is recommended when an archive is targeting + a specific processor and compiler specification (see Setting + Data Type Archive Architecture).

+
+
@@ -406,6 +414,51 @@ Archive.

+ +

Setting Data Type Archive Architecture

+ +
+

By default, data type archives are not assigned an architecture and adopt a default + Data Organization which determines primitive data type sizing (e.g., int, long, pointer, etc.) + as well as alignment and packing behavior. While data types copied between archives or a program + with disimilar Data Organizations will generally resolve correctly, unexpected results + may sometimes arise (e.g., bit-field packing within structures). In addition, this situation + frequently causes some confusion when viewing and editing data types where the Data + Organization differs from the intended target architecture.

+ +

With either a file or project data type archive open for editing, a specific architecture + may be assigned to the archive. This is done by selecting the open archive node from the + data type tree, right-click on it and select the Set Architecture... action. + This will popup a processor/compiler-specification selection dialog from which a selection + may be made and the OK button clicked. A final confirmation dialog will be displayed + before completing the change from which the Set Architecture or Cancel button + must be clicked to confirm or cancel the change. If confirmed, any architecture transition + will resolve all data types to the new data organization. Details related to custom variable + storage for Function Definitions (not yet implemented) will be preserved if possible but + may be discarded.

+ +

At present, the user will be forced to save any unsaved archive changes prior to completing + an architecture setting change to allow for a fallback if neccessary.

+ +
+ +

Clearing Data Type Archive Architecture

+ +
+ +

With either a file or project data type archive open for editing, a currently assigned architecture + may be cleared for a selected archive. This is done by selecting the open archive node from the + data type tree, right-click on it and select the Clear Architecture action. + A final confirmation dialog will be displayed + before completing the change from which the Clear Architecture or Cancel button + must be clicked to confirm or cancel the change. If confirmed, the archive will revert a default + Data Organization and any custom variable storage for Function Definitions + (not yet implemented) will be discarded. + +

At present, the user will be forced to save any unsaved changes prior to clearing the + architecture setting to allow for a fallback if neccessary.

+ +

Closing a Data Type Archive

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/ApplyFunctionSignatureCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/ApplyFunctionSignatureCmd.java index 2eee3110d0..1a87d98c0d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/ApplyFunctionSignatureCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/ApplyFunctionSignatureCmd.java @@ -140,6 +140,7 @@ public class ApplyFunctionSignatureCmd extends BackgroundCommand { func.updateFunction(conventionName, returnParam, params, FunctionUpdateType.DYNAMIC_STORAGE_FORMAL_PARAMS, false, source); func.setVarArgs(signature.hasVarArgs()); + func.setNoReturn(signature.hasNoReturn()); } catch (DuplicateNameException e) { // should not happen unless caused by a concurrent operation @@ -212,29 +213,21 @@ public class ApplyFunctionSignatureCmd extends BackgroundCommand { } private String getCallingConvention(Function function, CompilerSpec compilerSpec) { - PrototypeModel preferredModel = null; - if (signature.getGenericCallingConvention() != GenericCallingConvention.unknown) { - preferredModel = compilerSpec.matchConvention(signature.getGenericCallingConvention()); + + // Ignore signature's calling convention if unknown/not-defined + String callingConvention = signature.getCallingConventionName(); + if (compilerSpec.getCallingConvention(callingConvention) == null) { + callingConvention = null; } - PrototypeModel convention = function.getCallingConvention(); - if (convention == null || !preserveCallingConvention) { - convention = preferredModel; -// NOTE: This has been disable since it can cause imported signature information to be -// ignored and overwritten by subsequent analysis -// if (convention == null && compilerSpec.getCallingConventions().length > 1) { -// // use default source for signature if convention is really unknown so that we -// // know dynamic storage assignment is unreliable -// source = SourceType.DEFAULT; -// } + // Continue using function's current calling convention if valid and either + // reservation was requested or signature's convention is unknown/not-defined. + PrototypeModel currentConvention = function.getCallingConvention(); + if (currentConvention != null && (callingConvention == null || preserveCallingConvention)) { + callingConvention = function.getCallingConventionName(); } - // Calling convention is permitted to change - String conventionName = function.getCallingConventionName(); - if (!preserveCallingConvention && convention != null) { - conventionName = convention.getName(); - } - return conventionName; + return callingConvention; } private static void updateStackPurgeSize(Function function, Program program) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java index 3877a2d317..4fdb96a65c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java @@ -1473,6 +1473,7 @@ public class DataTypeMergeManager implements MergeResolver { ParameterDefinition[] sourceVars = sourceFunctionDefDt.getArguments(); ParameterDefinition[] destVars = new ParameterDefinition[sourceVars.length]; boolean sourceHasVarArgs = sourceFunctionDefDt.hasVarArgs(); + boolean sourceHasNoReturn = sourceFunctionDefDt.hasNoReturn(); DataType resolvedRDT = DataType.DEFAULT; if (sourceReturnType != null) { @@ -1492,6 +1493,7 @@ public class DataTypeMergeManager implements MergeResolver { } destDt.setArguments(destVars); destDt.setVarArgs(sourceHasVarArgs); + destDt.setNoReturn(sourceHasNoReturn); destDt.setLastChangeTime(oldLastChangeTime); destDt.setLastChangeTimeInSourceArchive(oldLastChangeTimeInSourceArchive); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java index f18266061d..1e8e8d1b46 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java @@ -29,6 +29,7 @@ import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.FunctionSignature; import ghidra.util.StringUtilities; import ghidra.util.UniversalID; @@ -348,7 +349,14 @@ class DataTypePanel extends JPanel { ParameterDefinition[] vars = fd.getArguments(); DataType returnType = fd.getReturnType(); + if (fd.hasNoReturn()) { + insertString(FunctionSignature.NORETURN_DISPLAY_STRING + " ", contentAttrSet); + } insertString(returnType.getDisplayName(), contentAttrSet); + String callingConventionName = fd.getCallingConventionName(); + if (!Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(callingConventionName)) { + insertString(callingConventionName + " ", contentAttrSet); + } insertString(" " + fd.getDisplayName(), nameAttrSet); insertString(" (", contentAttrSet); boolean hasVarArgs = fd.hasVarArgs(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java index fbdde9a48f..e165d9c492 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerDataTypeManager.java @@ -15,7 +15,13 @@ */ package ghidra.app.plugin.core.compositeeditor; +import java.io.IOException; + import ghidra.program.model.data.*; +import ghidra.program.model.lang.ProgramArchitecture; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager { @@ -23,10 +29,10 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager { * The data type manager for original composite data type being edited. * This is where the edited datatype will be written back to. */ - private DataTypeManager originalDTM; - private Composite originalComposite; - private Composite viewComposite; - private int transactionID; + private final DataTypeManager originalDTM; + private final Composite originalComposite; + private final Composite viewComposite; + private final int transactionID; /** * Creates a data type manager that the structure editor will use @@ -39,16 +45,42 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager { this.originalComposite = originalComposite; transactionID = startTransaction(""); originalDTM = originalComposite.getDataTypeManager(); - universalID = originalDTM.getUniversalID(); // mimic original DTM + + ProgramArchitecture arch = originalDTM.getProgramArchitecture(); + if (arch != null) { + try { + setProgramArchitecture(arch, null, true, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + throw new AssertException(e); // unexpected + } + catch (IOException e) { + errHandler.dbError(e); + } + } + viewComposite = (Composite) super.resolve(originalComposite, null); } - + + @Override + protected final boolean isArchitectureChangeAllowed() { + return false; + } + @Override public void close() { endTransaction(transactionID, true); super.close(); } + /** + * Get the {@link DataTypeManager} associated with the original composite datatype being edited. + * @return original datatype manager + */ + public DataTypeManager getOriginalDataTypeManager() { + return originalDTM; + } + @Override public ArchiveType getType() { return originalDTM.getType(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java index b0cb27cd17..83acb51fb7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java @@ -1344,4 +1344,8 @@ abstract class CompositeViewerModel extends AbstractTableModel return originalCompositeId; } + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + // don't care + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java index c02e2a4559..22f96f2627 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java @@ -15,19 +15,12 @@ */ package ghidra.app.plugin.core.cparser; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; +import java.io.*; import java.util.ArrayList; import java.util.StringTokenizer; import javax.swing.SwingUtilities; -import org.apache.commons.io.DirectoryWalker.CancelException; - import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; @@ -39,6 +32,7 @@ import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.cparser.C.CParser; +import ghidra.app.util.cparser.CPP.ParseException; import ghidra.app.util.cparser.CPP.PreProcessor; import ghidra.framework.Application; import ghidra.framework.options.SaveState; @@ -46,13 +40,9 @@ import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.database.data.ProgramDataTypeManager; -import ghidra.program.model.data.BuiltInDataTypeManager; -import ghidra.program.model.data.DataTypeManager; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; -import ghidra.util.HTMLUtilities; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; +import ghidra.util.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -206,6 +196,7 @@ public class CParserPlugin extends ProgramPlugin { cpp.setOutputStream(bos); cpp.setMonitor(monitor); + int fileCount = 0; try { for (String filename : filenames) { if (monitor.isCancelled()) { @@ -230,14 +221,26 @@ public class CParserPlugin extends ProgramPlugin { } } } - else { + else if (file.exists()) { + ++fileCount; parseFile(filename, monitor, cpp); } + else { + Msg.error(this, "Skipping file not found:" + filename); + } } } catch (RuntimeException re) { os.close(); - throw new ghidra.app.util.cparser.CPP.ParseException(re.getMessage()); + throw new ParseException(re.getMessage()); + } + + if (fileCount == 0) { + throw new ParseException("Failed to find any header files to parse!"); + } + else if (fileCount != filenames.length) { + Msg.warn(this, + "Only found " + fileCount + " of " + filenames.length + " specified header files"); } // process all the defines and add any that are integer values into diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java index c5a2f720e6..b02ba60e0a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java @@ -79,6 +79,7 @@ class CParserTask extends Task { plugin.parse(filenames, options, dtMgr, monitor); if (dataFileName != null) { + // TODO: does not consider existing datatypes if (dtMgr.getDataTypeCount(true) != 0) { try { ((FileDataTypeManager) dtMgr).save(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index 6a5ea1464f..7c20d83b60 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -180,6 +180,10 @@ public class DataTypesProvider extends ComponentProviderAdapter { addLocalAction(new LockArchiveAction(plugin)); // Archive addLocalAction(new UnlockArchiveAction(plugin)); // Archive + // Arch group + addLocalAction(new SetArchiveArchitectureAction(plugin)); // Archive + addLocalAction(new ClearArchiveArchitectureAction(plugin)); // Archive + // Repository group : version control actions addVersionControlActions(); // Archive diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java new file mode 100644 index 0000000000..f88e1aea76 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ClearArchiveArchitectureAction.java @@ -0,0 +1,187 @@ +/* ### + * 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.app.plugin.core.datamgr.actions; + +import java.io.IOException; + +import javax.swing.tree.TreePath; + +import docking.ActionContext; +import docking.action.DockingAction; +import docking.action.MenuData; +import docking.widgets.OptionDialog; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; +import generic.theme.GThemeDefaults.Colors.Messages; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.plugin.core.datamgr.DataTypesActionContext; +import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.framework.model.DomainFile; +import ghidra.framework.store.LockException; +import ghidra.program.model.data.StandAloneDataTypeManager; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.*; + +public class ClearArchiveArchitectureAction extends DockingAction { + + private final DataTypeManagerPlugin plugin; + + public ClearArchiveArchitectureAction(DataTypeManagerPlugin plugin) { + super("Clear Archive Architecture", plugin.getName()); + this.plugin = plugin; + + setPopupMenuData(new MenuData(new String[] { "Clear Architecture" }, null, "SetArch")); + + setDescription( + "Clear program-architecture associated with a data type archive (existing custom storage details will be discarded)"); + + setEnabled(true); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof DataTypesActionContext)) { + return false; + } + + Object contextObject = context.getContextObject(); + GTree gtree = (GTree) contextObject; + TreePath[] selectionPaths = gtree.getSelectionPaths(); + + if (selectionPaths.length != 1) { + return false; + } + GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent(); + if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) { + return false; + } + ArchiveNode archiveNode = (ArchiveNode) node; + StandAloneDataTypeManager dtm = + (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); + + return dtm.getProgramArchitectureSummary() != null && dtm.isUpdatable(); + } + + @Override + public void actionPerformed(ActionContext context) { + GTree gtree = (GTree) context.getContextObject(); + TreePath[] selectionPaths = gtree.getSelectionPaths(); + if (selectionPaths.length != 1) { + return; + } + GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent(); + if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) { + return; + } + + if (node instanceof ProjectArchiveNode) { + ProjectArchiveNode paNode = (ProjectArchiveNode) node; + ProjectArchive pa = (ProjectArchive) paNode.getArchive(); + if (!pa.hasExclusiveAccess()) { + Msg.showError(this, null, "Clear Program Architecture Failed", + "Clearing program-architecture on Project Archive requires exclusive checkout."); + return; + } + } + + ArchiveNode archiveNode = (ArchiveNode) node; + StandAloneDataTypeManager dtm = + (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); + + if (dtm.isChanged()) { + if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialogWithCancelAsDefaultButton( + null, "Save Archive Changes", + "Archive has unsaved changes which must be saved before continuing." + + "\nThis is required to allow for a reversion to the previous saved state.", + "Save")) { + return; + } + try { + archiveNode.getArchive().save(); + } + catch (IOException e) { + Msg.showError(this, null, "Save Archive Failed", + "Failed to save changes for Archive: " + dtm.getName() + "\n" + e.getMessage()); + return; + } + } + + // TODO: Update message indicating that custom storage specification will not be + // retained/permitted (once supported) + String msg = "Clear program-architecture for Archive?
" + dtm.getPath() + + "

Archive will revert to using default data organization."; + int response = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, + "Confirm Clearing Archive Architecture", msg, "Clear Architecture", + OptionDialog.WARNING_MESSAGE); + if (response != OptionDialog.OPTION_ONE) { + return; + } + + new TaskLauncher(new ClearProgramArchitectureTask(archiveNode.getArchive(), dtm)); + } + + private class ClearProgramArchitectureTask extends Task { + + private final Archive archive; + private final StandAloneDataTypeManager dtm; + + public ClearProgramArchitectureTask(Archive archive, StandAloneDataTypeManager dtm) { + super("Clearing Program-Architecture for Archive", true, false, true, false); + this.archive = archive; + this.dtm = dtm; + } + + @Override + public void run(TaskMonitor monitor) throws CancelledException { + boolean success = false; + try { + dtm.clearProgramArchitecture(monitor); + success = true; + } + catch (CancelledException e) { + throw e; + } + catch (Exception e) { + Msg.showError(this, null, "Archive Update Failed", + "Failed to clear program-architecture for Archive: " + dtm.getName() + "\n" + + e.getMessage()); + } + finally { + if (!success) { + if (archive instanceof FileArchive) { + try { + ((FileArchive) archive).releaseWriteLock(); + ((FileArchive) archive).acquireWriteLock(); + } + catch (LockException | IOException e) { + archive.close(); + } + } + else { // if (archive instanceof ProjectArchive) { + archive.close(); + DomainFile df = ((ProjectArchive) archive).getDomainFile(); + plugin.openArchive(df); + } + } + } + } + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java index bf0fba6450..708eb47577 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteArchiveAction.java @@ -37,8 +37,7 @@ public class DeleteArchiveAction extends DockingAction { public DeleteArchiveAction(DataTypeManagerPlugin plugin) { super("Delete Archive", plugin.getName()); -// ACTIONS - auto generated - setPopupMenuData(new MenuData(new String[] { "Delete Archive" }, null, "Edit")); + setPopupMenuData(new MenuData(new String[] { "Delete Archive" }, null, "File")); setKeyBindingData(new KeyBindingData(KeyEvent.VK_DELETE, 0)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java new file mode 100644 index 0000000000..0520cf2ad0 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SetArchiveArchitectureAction.java @@ -0,0 +1,259 @@ +/* ### + * 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.app.plugin.core.datamgr.actions; + +import java.io.IOException; + +import javax.swing.tree.TreePath; + +import docking.ActionContext; +import docking.action.DockingAction; +import docking.action.MenuData; +import docking.widgets.OptionDialog; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; +import generic.theme.GThemeDefaults.Colors.Messages; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.plugin.core.datamgr.DataTypesActionContext; +import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.tree.*; +import ghidra.app.plugin.core.processors.SetLanguageDialog; +import ghidra.framework.model.DomainFile; +import ghidra.framework.store.LockException; +import ghidra.program.model.data.StandAloneDataTypeManager; +import ghidra.program.model.data.StandAloneDataTypeManager.LanguageUpdateOption; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.util.Msg; +import ghidra.util.Swing; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.*; + +public class SetArchiveArchitectureAction extends DockingAction { + + private final DataTypeManagerPlugin plugin; + + public SetArchiveArchitectureAction(DataTypeManagerPlugin plugin) { + super("Set Archive Architecture", plugin.getName()); + this.plugin = plugin; + + setPopupMenuData(new MenuData(new String[] { "Set Architecture..." }, null, "SetArch")); + + setDescription("Set program-architecture associated with a data type archive"); + + setEnabled(true); + } + + private TreePath getSelectionPath(ActionContext context) { + Object contextObject = context.getContextObject(); + GTree gtree = (GTree) contextObject; + TreePath[] selectionPaths = gtree.getSelectionPaths(); + if (selectionPaths.length != 1) { + return null; + } + return selectionPaths[0]; + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof DataTypesActionContext)) { + return false; + } + TreePath selectionPath = getSelectionPath(context); + if (selectionPath == null) { + return false; + } + GTreeNode node = (GTreeNode) selectionPath.getLastPathComponent(); + if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) { + return false; + } + ArchiveNode archiveNode = (ArchiveNode) node; + StandAloneDataTypeManager dtm = + (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); + return dtm.isUpdatable(); + } + + @Override + public void actionPerformed(ActionContext context) { + TreePath selectionPath = getSelectionPath(context); + if (selectionPath == null) { + return; + } + GTreeNode node = (GTreeNode) selectionPath.getLastPathComponent(); + if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) { + return; + } + + if (node instanceof ProjectArchiveNode) { + ProjectArchiveNode paNode = (ProjectArchiveNode) node; + ProjectArchive pa = (ProjectArchive) paNode.getArchive(); + if (!pa.hasExclusiveAccess()) { + Msg.showError(this, null, "Set Program Architecture Failed", + "Setting program-architecture on Project Archive requires exclusive checkout."); + return; + } + } + + ArchiveNode archiveNode = (ArchiveNode) node; + StandAloneDataTypeManager dtm = + (StandAloneDataTypeManager) archiveNode.getArchive().getDataTypeManager(); + + if (dtm.isChanged()) { + if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialogWithCancelAsDefaultButton( + null, "Save Archive Changes", + "Archive has unsaved changes which must be saved before continuing." + + "\nThis is required to allow for a reversion to the previous saved state.", + "Save")) { + return; + } + try { + archiveNode.getArchive().save(); + } + catch (IOException e) { + Msg.showError(this, null, "Save Archive Failed", + "Failed to save changes for Archive: " + dtm.getName() + "\n" + e.getMessage()); + return; + } + } + + SetLanguageDialog dialog = new SetLanguageDialog(plugin.getTool(), + dtm.getProgramArchitecture(), + "Select Program Architecture for Archive: " + dtm.getName()); + LanguageID languageId = dialog.getLanguageDescriptionID(); + CompilerSpecID compilerSpecId = dialog.getCompilerSpecDescriptionID(); + if ((languageId == null) || (compilerSpecId == null)) { + return; + } + try { + Language language = DefaultLanguageService.getLanguageService().getLanguage(languageId); + + StringBuilder buf = new StringBuilder(); + buf.append(languageId.getIdAsString()); + buf.append(" / "); + buf.append(compilerSpecId.getIdAsString()); + String newProgramArchitectureSummary = buf.toString(); + + String programArchitectureSummary = dtm.getProgramArchitectureSummary(); + String msg = + "Set program-architecture for Archive?
" + dtm.getPath() + "
";
+			if (programArchitectureSummary != null) {
+				msg +=
+					"\nChange Language/Compiler\n  from:  " +
+					programArchitectureSummary + "\n    to:  ";
+			}
+			else {
+				msg += "\n\nLanguage/Compiler: ";
+			}
+			msg += "";
+			msg += newProgramArchitectureSummary;
+			msg += "
"; + int response = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, + "Confirm Archive Architecture Change", msg, "Set Architecture", + OptionDialog.WARNING_MESSAGE); + if (response != OptionDialog.OPTION_ONE) { + return; + } + + new TaskLauncher(new SetProgramArchitectureTask(archiveNode.getArchive(), dtm, language, + compilerSpecId)); + } + catch (LanguageNotFoundException e) { + Msg.showError(this, null, "Archive Update Failed", + "Failed to set program-architecture for Archive: " + dtm.getName() + "\n" + + e.getMessage()); + } + } + + private class SetProgramArchitectureTask extends Task { + + private final Archive archive; + private final StandAloneDataTypeManager dtm; + private final Language language; + private final CompilerSpecID compilerSpecId; + + public SetProgramArchitectureTask(Archive archive, StandAloneDataTypeManager dtm, + Language language, CompilerSpecID compilerSpecId) { + super("Updating Program-Architecture for Archive", true, false, true, false); + this.archive = archive; + this.dtm = dtm; + this.language = language; + this.compilerSpecId = compilerSpecId; + } + + @Override + public void run(TaskMonitor monitor) throws CancelledException { + + boolean success = false; + try { + try { + dtm.setProgramArchitecture(language, compilerSpecId, + LanguageUpdateOption.TRANSLATE, monitor); + success = true; + } + catch (IncompatibleLanguageException e) { + int resp = OptionDialog.showOptionDialog(null, "Archive Architecture Change", + "Unable to translate storage for specified architecture change.
" + dtm.getPath() + + "

Would you like to Clear custom storage information or Cancel change?", + "Clear"); + if (resp == OptionDialog.CANCEL_OPTION) { + success = true; // keep archive open + return; + } + LanguageUpdateOption updateOption = LanguageUpdateOption.CLEAR; + if (resp == OptionDialog.OPTION_TWO) { + updateOption = LanguageUpdateOption.UNCHANGED; + } + dtm.setProgramArchitecture(language, compilerSpecId, updateOption, monitor); + success = true; + } + } + catch (CancelledException e) { + throw e; + } + catch (Exception e) { + Msg.showError(this, null, "Archive Update Failed", + "Failed to set program-architecture for Archive: " + dtm.getName() + "\n" + + e.getMessage()); + } + finally { + if (!success) { + Swing.runNow(() -> { + /* flush event queue before closing archive */ }); + if (archive instanceof FileArchive) { + try { + ((FileArchive) archive).releaseWriteLock(); + ((FileArchive) archive).acquireWriteLock(); + } + catch (LockException | IOException e) { + archive.close(); + } + } + else { // if (archive instanceof ProjectArchive) { + archive.close(); + DomainFile df = ((ProjectArchive) archive).getDomainFile(); + plugin.openArchive(df); + } + } + } + } + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java index b45d8b0031..590fad3050 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ArchiveUtils.java @@ -96,17 +96,21 @@ public class ArchiveUtils { return true; } catch (ReadOnlyException e) { - Msg.showError(log, null, "Unable to Lock File for Writing", e.getMessage()); + Msg.showError(log, null, "Unable to Lock Archive for Writing", e.getMessage()); } catch (LockException exc) { - Msg.showError(log, null, "Unable to Lock File for Writing", + Msg.showError(log, null, "Unable to Lock Archive for Writing", "Unable to obtain lock for archive: " + archive.getName() + "\n" + exc.getMessage()); } catch (IOException ioe) { - Msg.showError(log, null, "Unable to Lock File for Writing", - "Problem attempting to lock archive: " + archive.getName() + "\n" + - ioe.getMessage()); + Throwable cause = ioe.getCause(); + if (cause == null) { + cause = ioe; + } + Msg.showError(log, null, "Unable to Lock Archive for Writing", + "Problem attempting to open archive for update: " + archive.getName() + "\n" + + cause.getMessage()); } return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java index 3fce3c2984..b89dce170c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeIndexer.java @@ -219,5 +219,10 @@ public class DataTypeIndexer { public void sourceArchiveChanged(DataTypeManager dtm, SourceArchive dataTypeSource) { markStale(); } + + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + markStale(); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java index 4bb5abd0ff..c2d36b183c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java @@ -437,12 +437,13 @@ public class DataTypeManagerHandler { return openArchive(new ResourceFile(file), acquireWriteLock, isUserAction); } - public Archive openArchive(ResourceFile file, boolean acquireWriteLock, boolean isUserAction) + public FileArchive openArchive(ResourceFile file, boolean acquireWriteLock, + boolean isUserAction) throws IOException, DuplicateIdException { file = file.getCanonicalFile(); - Archive archive = getArchiveForFile(file); + FileArchive archive = getArchiveForFile(file); if (archive == null) { archive = new FileArchive(this, file, acquireWriteLock); Archive existingArchive = @@ -451,11 +452,10 @@ public class DataTypeManagerHandler { archive.close(); throw new DuplicateIdException(archive.getName(), existingArchive.getName()); } - addArchivePath(file); addArchive(archive); } - if (isUserAction && (archive instanceof FileArchive)) { + if (isUserAction) { userOpenedFileArchiveNames.add(getSaveableArchive(file.getAbsolutePath())); } return archive; @@ -524,7 +524,7 @@ public class DataTypeManagerHandler { return null; } - private Archive getArchiveForFile(ResourceFile file) { + private FileArchive getArchiveForFile(ResourceFile file) { for (Archive archive : openArchives) { if (archive instanceof FileArchive) { FileArchive fileArchive = (FileArchive) archive; @@ -1230,6 +1230,13 @@ public class DataTypeManagerHandler { listener.sourceArchiveChanged(dataTypeManager, dataTypeSource); } } + + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) { + listener.programArchitectureChanged(dataTypeManager); + } + } } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java index 28fd51880d..2660379a8a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DomainFileArchive.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,4 +23,6 @@ public interface DomainFileArchive extends Archive { public abstract DomainFile getDomainFile(); public abstract DomainObject getDomainObject(); + + public abstract boolean hasExclusiveAccess(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java index 2bd2685b94..f68e2ac82a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/FileArchive.java @@ -145,7 +145,7 @@ public class FileArchive implements Archive { } @Override - public DataTypeManager getDataTypeManager() { + public FileDataTypeManager getDataTypeManager() { return fileDataTypeManager; } @@ -274,6 +274,11 @@ public class FileArchive implements Archive { public void sourceArchiveChanged(DataTypeManager dtm, SourceArchive dataTypeSource) { setChanged(true); } + + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + setChanged(true); + } } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java index b07baf6cea..0c8df9b5f9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProgramArchive.java @@ -72,6 +72,11 @@ public class ProgramArchive implements DomainFileArchive { // Can't directly close the program archive. Instead you must close the Program. } + @Override + public boolean hasExclusiveAccess() { + return program.hasExclusiveAccess(); + } + @Override public boolean isChanged() { return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java index 8dd3531961..5afcf4ecb4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/ProjectArchive.java @@ -66,6 +66,11 @@ public class ProjectArchive implements DomainFileArchive { return -1; // Project Archives appear between the ProgramArchive and FileArchives. } + @Override + public boolean hasExclusiveAccess() { + return dataTypeArchive.hasExclusiveAccess(); + } + @Override public boolean isModifiable() { DomainFile domainFile = getDomainObject().getDomainFile(); @@ -192,5 +197,10 @@ public class ProjectArchive implements DomainFileArchive { public void sourceArchiveChanged(DataTypeManager dtm, SourceArchive dataTypeSource) { fireStateChanged(); } + + @Override + public void programArchitectureChanged(DataTypeManager dtm) { + fireStateChanged(); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java index d05f007a9d..29c8a52a48 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.datamgr.editor; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import docking.ComponentProvider; import docking.actions.DockingToolActions; @@ -28,11 +27,9 @@ import ghidra.framework.model.DomainObject; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; -import ghidra.program.model.listing.FunctionSignature; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.util.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.*; /** * Manages program and archive data type editors. @@ -541,27 +538,53 @@ public class DataTypeEditorManager * Use of this editor requires the presence of the tool-based datatype manager service. */ private class DTMEditFunctionSignatureDialog extends AbstractEditFunctionSignatureDialog { - private final FunctionDefinition functionDefinition; + private final FunctionDefinition functionDefinition; // may be null private final FunctionSignature oldSignature; private final Category category; + /** + * Construct function signature editor model + * @param pluginTool plugin tool + * @param title Dialog title + * @param category datatype category + * @param functionDefinition function definition to be modified (null for new definition) + */ DTMEditFunctionSignatureDialog(PluginTool pluginTool, String title, Category category, FunctionDefinition functionDefinition) { - super(pluginTool, title, false, false, false); + super(pluginTool, title, false, true, false); this.functionDefinition = functionDefinition; this.category = category; this.oldSignature = buildSignature(); + if (isAdhocCallingConventionPermitted()) { + callingConventionComboBox.setEditable(true); + } + } + + /** + * Determine if an adhoc calling convention entry is permitted (i.e., text entry) + * @return true if calling convention name may be edited with text entry, else false + */ + private boolean isAdhocCallingConventionPermitted() { + // DataTypeManager dtm = functionDefinition.getDataTypeManager(); + // return dtm == null || dtm.getProgramArchitecture() == null; + // TODO: not sure we should allow unrestricted entries which could lead to using misspelled names + return false; } private FunctionSignature buildSignature() { if (functionDefinition != null) { if (category.getDataTypeManager() != functionDefinition.getDataTypeManager()) { throw new IllegalArgumentException( - "functionDefinition and category must have same Datatypemanager"); + "FunctionDefinition and Category must have same DataTypeManager"); } return functionDefinition; } - return new FunctionDefinitionDataType("newFunction"); + return new FunctionDefinitionDataType("newFunction", category.getDataTypeManager()); + } + + @Override + protected boolean hasNoReturn() { + return functionDefinition != null ? functionDefinition.hasNoReturn() : false; } @Override @@ -586,17 +609,21 @@ public class DataTypeEditorManager @Override protected String getCallingConventionName() { - return getFunctionSignature().getGenericCallingConvention().toString(); + return getFunctionSignature().getCallingConventionName(); } @Override protected List getCallingConventionNames() { - GenericCallingConvention[] values = GenericCallingConvention.values(); - List choices = new ArrayList<>(); - for (GenericCallingConvention value : values) { - choices.add(value.toString()); + // can't rely on functionDefinition which may be null for new definition + DataTypeManager dtMgr = getDataTypeManager(); + if (dtMgr instanceof CompositeViewerDataTypeManager) { + dtMgr = ((CompositeViewerDataTypeManager)dtMgr).getOriginalDataTypeManager(); } - return choices; + ArrayList list = new ArrayList<>(); + list.add(Function.UNKNOWN_CALLING_CONVENTION_STRING); + list.add(Function.DEFAULT_CALLING_CONVENTION_STRING); + list.addAll(dtMgr.getDefinedCallingConventionNames()); + return list; } @Override @@ -620,36 +647,53 @@ public class DataTypeEditorManager return false; } - GenericCallingConvention callingConvention = - GenericCallingConvention.getGenericCallingConvention(getCallingConvention()); - newDefinition.setGenericCallingConvention(callingConvention); + String callingConvention = getCallingConvention(); + boolean hasNoReturn = hasNoReturnSelected(); + try { + newDefinition.setCallingConvention(callingConvention); + newDefinition.setNoReturn(hasNoReturn); - DataTypeManager manager = getDataTypeManager(); - SourceArchive sourceArchive = manager.getLocalSourceArchive(); - if (functionDefinition == null) { - newDefinition.setSourceArchive(sourceArchive); - newDefinition.setCategoryPath(category.getCategoryPath()); - int id = manager.startTransaction("Create Function Definition"); - manager.addDataType(newDefinition, DataTypeConflictHandler.REPLACE_HANDLER); - manager.endTransaction(id, true); - } - else { - int id = manager.startTransaction("Edit Function Definition"); - try { - if (!functionDefinition.getName().equals(newDefinition.getName())) { - functionDefinition.setName(newDefinition.getName()); + DataTypeManager manager = getDataTypeManager(); + SourceArchive sourceArchive = manager.getLocalSourceArchive(); + if (functionDefinition == null) { + newDefinition.setSourceArchive(sourceArchive); + newDefinition.setCategoryPath(category.getCategoryPath()); + int id = manager.startTransaction("Create Function Definition"); + try { + manager.addDataType(newDefinition, DataTypeConflictHandler.REPLACE_HANDLER); + } + finally { + manager.endTransaction(id, true); } - functionDefinition.setArguments(newDefinition.getArguments()); - functionDefinition.setGenericCallingConvention( - newDefinition.getGenericCallingConvention()); - functionDefinition.setReturnType(newDefinition.getReturnType()); - functionDefinition.setVarArgs(newDefinition.hasVarArgs()); } - catch (InvalidNameException | DuplicateNameException e) { - // not sure why we are squashing this? ...assuming this can't happen - Msg.error(this, "Unexpected Exception", e); + else { + int id = manager.startTransaction("Edit Function Definition"); + try { + if (!functionDefinition.getName().equals(newDefinition.getName())) { + functionDefinition.setName(newDefinition.getName()); + } + functionDefinition.setArguments(newDefinition.getArguments()); + if (!Objects.equals(callingConvention, + functionDefinition.getCallingConventionName())) { + functionDefinition.setCallingConvention(callingConvention); + } + functionDefinition.setReturnType(newDefinition.getReturnType()); + functionDefinition.setVarArgs(newDefinition.hasVarArgs()); + functionDefinition.setNoReturn(hasNoReturn); + } + catch (InvalidNameException | DuplicateNameException e) { + // not sure why we are squashing this? ...assuming this can't happen + Msg.error(this, "Unexpected Exception", e); + } + finally { + manager.endTransaction(id, true); + } } - manager.endTransaction(id, true); + } + catch (InvalidInputException e) { + setStatusText("Unknown calling convention specified: " + callingConvention, + MessageType.ERROR); + return false; } return true; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java index e2bca8e965..56bb376442 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java @@ -21,12 +21,17 @@ import javax.swing.Icon; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.program.model.data.*; +import ghidra.util.HTMLUtilities; import ghidra.util.task.SwingUpdateManager; public class ArchiveNode extends CategoryNode { + protected static final String DEFAULT_DATA_ORG_DESCRIPTION = + "[Using Default Data Organization]"; + protected Archive archive; protected ArchiveNodeCategoryChangeListener listener; private DataTypeManager dataTypeManager; // may be null @@ -45,6 +50,43 @@ public class ArchiveNode extends CategoryNode { this.archive = archive; } + protected String buildTooltip(String path) { + DataTypeManager dtm = archive.getDataTypeManager(); + if (dtm == null) { + return null; + } + StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); + buf.append(HTMLUtilities.escapeHTML(path)); + buf.append(HTMLUtilities.BR); + String programArchSummary = dtm.getProgramArchitectureSummary(); + if (programArchSummary != null) { + buf.append(HTMLUtilities.HTML_SPACE); + buf.append(HTMLUtilities.HTML_SPACE); + buf.append(HTMLUtilities.escapeHTML(programArchSummary)); + addArchiveWarnings(dtm, buf); + } + else { + buf.append(DEFAULT_DATA_ORG_DESCRIPTION); + } + return buf.toString(); + } + + private void addArchiveWarnings(DataTypeManager dtm, StringBuilder buf) { + if (dtm instanceof StandAloneDataTypeManager archiveDtm) { + if (archiveDtm.isProgramArchitectureMissing()) { + buf.append(HTMLUtilities.BR); + buf.append( + "** Missing Language/Compiler Specification **"); + } + else if (archiveDtm.isProgramArchitectureUpgradeRequired()) { + buf.append(HTMLUtilities.BR); + buf.append("** Language Upgrade Required **"); + } + } + } + protected void archiveStateChanged() { nodeChanged(); } @@ -172,11 +214,11 @@ public class ArchiveNode extends CategoryNode { return -1; // All ArchiveNodes are before any other types of nodes } - @Override /** * The hashcode must not be based on the name since it can change based upon the underlying * archive. This must be consistent with the equals method implementation. */ + @Override public int hashCode() { return getArchive().hashCode(); } @@ -424,5 +466,13 @@ public class ArchiveNode extends CategoryNode { public void sourceArchiveChanged(DataTypeManager manager, SourceArchive sourceArchive) { nodeChangedUpdater.update(); } + + @Override + public void programArchitectureChanged(DataTypeManager manager) { + // need to force all cached datatype tooltips to be cleared + // due to change in data organization + unloadChildren(); + nodeChangedUpdater.update(); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java index 67a9926a58..56379b9302 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.datamgr.tree; import javax.swing.Icon; import ghidra.app.plugin.core.datamgr.archive.BuiltInArchive; +import ghidra.util.HTMLUtilities; import resources.MultiIcon; public class BuiltInArchiveNode extends ArchiveNode { @@ -35,7 +36,13 @@ public class BuiltInArchiveNode extends ArchiveNode { @Override public String getToolTip() { - return "Built In Data Types"; + StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); + buf.append("Built In Data Types"); + buf.append(HTMLUtilities.BR); + buf.append(HTMLUtilities.HTML_SPACE); + buf.append(HTMLUtilities.HTML_SPACE); + buf.append(DEFAULT_DATA_ORG_DESCRIPTION); + return buf.toString(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java index d795484be3..47c002d160 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java @@ -22,11 +22,10 @@ import ghidra.app.plugin.core.datamgr.archive.DomainFileArchive; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainObject; import ghidra.program.model.listing.Program; -import ghidra.util.HTMLUtilities; import resources.MultiIcon; import resources.icons.TranslateIcon; -public class DomainFileArchiveNode extends ArchiveNode { +public abstract class DomainFileArchiveNode extends ArchiveNode { //@formatter:off private static Icon CHECKED_OUT_ICON = new GIcon("icon.plugin.datatypes.tree.node.archive.file.checked.out"); @@ -98,13 +97,7 @@ public class DomainFileArchiveNode extends ArchiveNode { } @Override - public String getToolTip() { - DomainFile file = ((DomainFileArchive) archive).getDomainFile(); - if (file != null) { - return "" + HTMLUtilities.escapeHTML(file.getPathname()); - } - return "[Unsaved New Domain File Archive]"; - } + public abstract String getToolTip(); @Override public boolean canDelete() { @@ -120,7 +113,7 @@ public class DomainFileArchiveNode extends ArchiveNode { multiIcon.addIcon(baseIcon); if (isReadOnly) { - multiIcon.addIcon(new TranslateIcon(READ_ONLY_ICON, 6, 6)); + multiIcon.addIcon(new TranslateIcon(READ_ONLY_ICON, 14, 3)); } else if (isHijacked) { multiIcon.addIcon(new TranslateIcon(HIJACKED_ICON, 8, -4)); @@ -137,6 +130,8 @@ public class DomainFileArchiveNode extends ArchiveNode { } } + // TODO: add program architecture state + return multiIcon; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java index 1eb46bc540..ec7bf2199a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java @@ -20,7 +20,6 @@ import javax.swing.Icon; import generic.jar.ResourceFile; import generic.theme.GIcon; import ghidra.app.plugin.core.datamgr.archive.FileArchive; -import ghidra.util.HTMLUtilities; import resources.MultiIcon; import resources.icons.TranslateIcon; @@ -46,16 +45,16 @@ public class FileArchiveNode extends ArchiveNode { if (hasWriteLock) { multiIcon.addIcon(new TranslateIcon(CHECKED_OUT_EXCLUSIVE_ICON, 8, -4)); } + + // TODO: add program architecture state + return multiIcon; } @Override public String getToolTip() { ResourceFile file = fileArchive.getFile(); - if (file != null) { - return "" + HTMLUtilities.escapeHTML(file.getAbsolutePath()); - } - return "[Unsaved New Archive]"; + return buildTooltip(file != null ? file.getAbsolutePath() : "[Unsaved New Archive]"); } public boolean hasWriteLock() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java index 57198b7a0a..61f27055f1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java @@ -17,6 +17,7 @@ package ghidra.app.plugin.core.datamgr.tree; import ghidra.app.plugin.core.datamgr.archive.ProgramArchive; import ghidra.framework.model.DomainFile; +import ghidra.program.model.data.DataTypeManager; import ghidra.util.HTMLUtilities; public class ProgramArchiveNode extends DomainFileArchiveNode { @@ -27,10 +28,19 @@ public class ProgramArchiveNode extends DomainFileArchiveNode { @Override public String getToolTip() { + DataTypeManager dtm = archive.getDataTypeManager(); DomainFile file = ((ProgramArchive) archive).getDomainFile(); + StringBuilder buf = new StringBuilder(HTMLUtilities.HTML); if (file != null) { - return "" + HTMLUtilities.escapeHTML(file.getPathname()); + buf.append(HTMLUtilities.escapeHTML(file.toString())); } - return "[Unsaved New Program Archive]"; + else { + buf.append("[Unsaved New Program Archive]"); + } + buf.append(HTMLUtilities.BR); + buf.append(HTMLUtilities.HTML_SPACE); + buf.append(HTMLUtilities.HTML_SPACE); + buf.append(HTMLUtilities.escapeHTML(dtm.getProgramArchitectureSummary())); + return buf.toString(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java index 8763a3cf14..ae0671ac33 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java @@ -17,7 +17,6 @@ package ghidra.app.plugin.core.datamgr.tree; import ghidra.app.plugin.core.datamgr.archive.ProjectArchive; import ghidra.framework.model.DomainFile; -import ghidra.util.HTMLUtilities; public class ProjectArchiveNode extends DomainFileArchiveNode { @@ -35,10 +34,7 @@ public class ProjectArchiveNode extends DomainFileArchiveNode { @Override public String getToolTip() { DomainFile file = ((ProjectArchive) archive).getDomainFile(); - if (file != null) { - return "" + HTMLUtilities.escapeHTML(file.getPathname()); - } - return "[Unsaved New Project Archive]"; + return buildTooltip(file != null ? file.getPathname() : "[Unsaved New Project Archive]"); } public boolean hasWriteLock() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java index 5f11b43d9d..83442e6dfa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java @@ -361,6 +361,7 @@ public class DataTypeTreeCopyMoveTask extends Task { DataTypeManager dtm = toCategory.getDataTypeManager(); DataTypeManager nodeDtm = dataType.getDataTypeManager(); boolean sameManager = (dtm == nodeDtm); + DataType newDt = !sameManager ? dataType.clone(nodeDtm) : dataType.copy(nodeDtm); if (!sameManager && toCategory.isRoot()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java index bbe5b44b3c..dd4a6b9e3f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java @@ -24,7 +24,6 @@ import javax.swing.Icon; import generic.theme.GColor; import generic.theme.GIcon; import ghidra.app.services.DataTypeQueryService; -import ghidra.program.database.data.ProjectDataTypeManager; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.util.Msg; @@ -438,8 +437,9 @@ public class DataTypeUtils { msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" + "before performing this operation.\n" + dtm.getName(); } - else if (dtm instanceof ProjectDataTypeManager) { - ProjectDataTypeManager projectDtm = (ProjectDataTypeManager) dtm; + else if (dtm instanceof ProjectArchiveBasedDataTypeManager) { + ProjectArchiveBasedDataTypeManager projectDtm = + (ProjectArchiveBasedDataTypeManager) dtm; if (!projectDtm.isUpdatable() && !projectDtm.getDomainFile().canCheckout()) { msg = "The project archive is not modifiable!\n" + dtm.getName(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/AbstractEditFunctionSignatureDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/AbstractEditFunctionSignatureDialog.java index 63d18b1332..ee199456ee 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/AbstractEditFunctionSignatureDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/AbstractEditFunctionSignatureDialog.java @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.function; import java.awt.Component; import java.awt.event.ItemEvent; import java.util.List; +import java.util.Objects; import javax.swing.*; @@ -32,6 +33,7 @@ import ghidra.app.util.parser.FunctionSignatureParser; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FunctionDefinitionDataType; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.FunctionSignature; import ghidra.util.exception.CancelledException; @@ -110,7 +112,7 @@ public abstract class AbstractEditFunctionSignatureDialog extends DialogComponen protected abstract String getPrototypeString(); /** - * @return initial calling convention name + * @return initial calling convention name or null for unknown */ protected abstract String getCallingConventionName(); @@ -282,17 +284,33 @@ public abstract class AbstractEditFunctionSignatureDialog extends DialogComponen } private void setCallingConventionChoices() { + String callingConventionName = getCallingConventionName(); callingConventionComboBox.removeAllItems(); for (String element : getCallingConventionNames()) { + if (element.equals(callingConventionName)) { + callingConventionName = null; + } callingConventionComboBox.addItem(element); } + if (callingConventionName != null) { + callingConventionComboBox.addItem(callingConventionName); + } } /** - * @return current calling convention selection from dialog + * @return current calling convention selection from dialog. Null will be returned + * if unknown is selected. */ - protected String getCallingConvention() { - return (String) callingConventionComboBox.getSelectedItem(); + protected final String getCallingConvention() { + String callingConvention = (String) callingConventionComboBox.getSelectedItem(); + if (callingConvention != null) { + callingConvention = callingConvention.trim(); + } + if (callingConvention.length() == 0 || + Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(callingConvention)) { + callingConvention = null; + } + return callingConvention; } /** @@ -379,7 +397,6 @@ public abstract class AbstractEditFunctionSignatureDialog extends DialogComponen FunctionSignatureParser parser = new FunctionSignatureParser( getDataTypeManager(), tool.getService(DataTypeManagerService.class)); try { - // FIXME: Parser returns FunctionDefinition which only supports GenericCallingConventions return parser.parse(getFunctionSignature(), getSignature()); } catch (ParseException e) { @@ -402,22 +419,7 @@ public abstract class AbstractEditFunctionSignatureDialog extends DialogComponen */ protected final boolean isCallingConventionChanged() { String current = getCallingConventionName(); - if (current == null && this.getCallingConvention() == null) { - return false; - } - if (current == null && this.getCallingConvention().equals("default")) { - return false; - } - if (current == null && this.getCallingConvention().equals("unknown")) { - return false; - } - if (current == null) { - return true; - } - if (current.equals(getCallingConvention())) { - return false; - } - return true; + return !Objects.equals(current, getCallingConvention()); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialog.java index 270b9440a7..ab6e9894d2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialog.java @@ -15,6 +15,7 @@ */ package ghidra.app.plugin.core.function; +import java.util.ArrayList; import java.util.List; import ghidra.app.cmd.function.ApplyFunctionSignatureCmd; @@ -77,7 +78,11 @@ public class EditFunctionSignatureDialog extends AbstractEditFunctionSignatureDi @Override protected List getCallingConventionNames() { - return function.getProgram().getFunctionManager().getCallingConventionNames(); + List list = new ArrayList<>(); + list.add(Function.UNKNOWN_CALLING_CONVENTION_STRING); + list.add(Function.DEFAULT_CALLING_CONVENTION_STRING); + list.addAll(function.getProgram().getFunctionManager().getCallingConventionNames()); + return list; } @Override @@ -182,12 +187,6 @@ public class EditFunctionSignatureDialog extends AbstractEditFunctionSignatureDi public boolean applyTo(DomainObject obj) { try { String conventionName = getCallingConvention(); - if ("unknown".equals(conventionName)) { - conventionName = null; - } - else if ("default".equals(conventionName)) { - conventionName = function.getDefaultCallingConventionName(); - } function.setCallingConvention(conventionName); return true; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java index 0010138747..45fdd7468b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorModel.java @@ -124,15 +124,24 @@ public class FunctionEditorModel { return false; } - public List getCallingConventionNames() { - return functionManager.getCallingConventionNames(); + List getCallingConventionNames() { + Collection names = + function.getProgram().getFunctionManager().getCallingConventionNames(); + List list = new ArrayList<>(names); + if (callingConventionName != null && !names.contains(callingConventionName)) { + list.add(callingConventionName); + Collections.sort(list); + } + list.add(0, Function.DEFAULT_CALLING_CONVENTION_STRING); + list.add(0, Function.UNKNOWN_CALLING_CONVENTION_STRING); + return list; } - public String[] getCallFixupNames() { + String[] getCallFixupNames() { return program.getCompilerSpec().getPcodeInjectLibrary().getCallFixupNames(); } - public void setName(String name) { + void setName(String name) { if (this.name.equals(name)) { return; } @@ -147,20 +156,20 @@ public class FunctionEditorModel { notifyDataChanged(); } - public String getCallingConventionName() { + String getCallingConventionName() { return callingConventionName; } - public void setHasVarArgs(boolean b) { + void setHasVarArgs(boolean b) { hasVarArgs = b; notifyDataChanged(); } - public String getName() { + String getName() { return name; } - public void dispose() { + void dispose() { listener = new ModelChangeListener() { @Override public void tableRowsChanged() { @@ -316,7 +325,7 @@ public class FunctionEditorModel { return true; } - public boolean hasValidName() { + boolean hasValidName() { if (name.length() == 0) { statusText = "Missing function name"; return false; @@ -381,11 +390,11 @@ public class FunctionEditorModel { return true; } - public boolean isValid() { + boolean isValid() { return isValid; } - public String getFunctionSignatureTextFromModel() { + String getFunctionSignatureTextFromModel() { StringBuilder buf = new StringBuilder(); buf.append(returnInfo.getFormalDataType().getName()).append(" "); buf.append(getNameString()); @@ -420,7 +429,7 @@ public class FunctionEditorModel { return buf.toString(); } - public String getNameString() { + String getNameString() { return name.length() == 0 ? "?" : name; } @@ -428,15 +437,15 @@ public class FunctionEditorModel { return param.getName(); } - public boolean hasVarArgs() { + boolean hasVarArgs() { return hasVarArgs; } - public DataType getReturnType() { + DataType getReturnType() { return returnInfo.getDataType(); } - public DataType getFormalReturnType() { + DataType getFormalReturnType() { return returnInfo.getFormalDataType(); } @@ -444,14 +453,14 @@ public class FunctionEditorModel { return setParameterFormalDataType(returnInfo, formalReturnType); } - public String getStatusText() { + String getStatusText() { if (isInParsingMode) { return PARSING_MODE_STATUS_TEXT; } return statusText; } - public void setIsInLine(boolean isInLine) { + void setIsInLine(boolean isInLine) { if (isInLine == this.isInLine) { return; } @@ -462,12 +471,12 @@ public class FunctionEditorModel { notifyDataChanged(); } - public void setNoReturn(boolean isNoReturn) { + void setNoReturn(boolean isNoReturn) { this.isNoReturn = isNoReturn; notifyDataChanged(); } - public boolean isInlineAllowed() { + boolean isInlineAllowed() { return !getAffectiveFunction().isExternal(); } @@ -481,19 +490,19 @@ public class FunctionEditorModel { return function.isThunk() ? function.getThunkedFunction(true) : function; } - public boolean isInLine() { + boolean isInLine() { return isInLine; } - public boolean isNoReturn() { + boolean isNoReturn() { return isNoReturn; } - public String getCallFixupName() { + String getCallFixupName() { return callFixupName; } - public void setCallFixupName(String callFixupName) { + void setCallFixupName(String callFixupName) { if (callFixupName.equals(this.callFixupName)) { return; } @@ -555,11 +564,11 @@ public class FunctionEditorModel { } } - public int[] getSelectedParameterRows() { + int[] getSelectedParameterRows() { return selectedFunctionRows; } - public void addParameter() { + void addParameter() { if (listener != null) { listener.tableRowsChanged(); } @@ -700,7 +709,7 @@ public class FunctionEditorModel { notifyDataChanged(); } - public void moveSelectedParameterUp() { + void moveSelectedParameterUp() { if (!canMoveParameterUp()) { throw new AssertException("Attempted to move parameters up when not allowed."); } @@ -716,7 +725,7 @@ public class FunctionEditorModel { notifyDataChanged(); } - public void moveSelectedParameterDown() { + void moveSelectedParameterDown() { if (!canMoveParameterDown()) { throw new AssertException("Attempted to move parameters down when not allowed."); } @@ -736,7 +745,7 @@ public class FunctionEditorModel { return parameters; } - public boolean canRemoveParameters() { + boolean canRemoveParameters() { if (selectedFunctionRows.length == 0) { return false; } @@ -748,7 +757,7 @@ public class FunctionEditorModel { return true; } - public boolean canMoveParameterUp() { + boolean canMoveParameterUp() { // remember first row (return type) and auto-params cannot be moved. int minRowToMoveUp = 2 + autoParamCount; if (parameters.size() > 0 && parameters.get(0).getName().equals("this")) { @@ -757,7 +766,7 @@ public class FunctionEditorModel { return selectedFunctionRows.length == 1 && selectedFunctionRows[0] >= minRowToMoveUp; } - public boolean canMoveParameterDown() { + boolean canMoveParameterDown() { if (selectedFunctionRows.length != 1) { return false; } @@ -770,12 +779,12 @@ public class FunctionEditorModel { return selectedRow >= minRowToMoveDown && selectedRow < parameters.size(); } - public void setParameterName(ParamInfo param, String newName) { + void setParameterName(ParamInfo param, String newName) { param.setName(newName); notifyDataChanged(); } - public boolean setParameterFormalDataType(ParamInfo param, DataType formalDataType) { + boolean setParameterFormalDataType(ParamInfo param, DataType formalDataType) { boolean isReturn = (param.getOrdinal() == Parameter.RETURN_ORIDINAL); try { formalDataType = VariableUtilities.checkDataType(formalDataType, isReturn, 0, program); @@ -839,11 +848,11 @@ public class FunctionEditorModel { } } - public VariableStorage getReturnStorage() { + VariableStorage getReturnStorage() { return returnInfo.getStorage(); } - public Function getFunction() { + Function getFunction() { return function; } @@ -940,7 +949,7 @@ public class FunctionEditorModel { return allowCustomStorage; } - public boolean apply() { + boolean apply() { if (!modelChanged) { return true; } @@ -1085,16 +1094,10 @@ public class FunctionEditorModel { } public void setFunctionData(FunctionDefinitionDataType functionDefinition) { + name = functionDefinition.getName(); - GenericCallingConvention genericCallingConvention = - functionDefinition.getGenericCallingConvention(); - if (genericCallingConvention != null && - genericCallingConvention != GenericCallingConvention.unknown) { - PrototypeModel matchConvention = - function.getProgram().getCompilerSpec().matchConvention(genericCallingConvention); - setCallingConventionName(matchConvention.getName()); - } + setCallingConventionName(functionDefinition.getCallingConventionName()); if (!isSameSize(returnInfo.getFormalDataType(), functionDefinition.getReturnType())) { returnInfo.setStorage(VariableStorage.UNASSIGNED_STORAGE); @@ -1110,6 +1113,7 @@ public class FunctionEditorModel { parameters.add(new ParamInfo(this, paramDefinition)); } hasVarArgs = functionDefinition.hasVarArgs(); + fixupOrdinals(); if (allowCustomStorage) { @@ -1162,11 +1166,11 @@ public class FunctionEditorModel { return null; } - public boolean isInParsingMode() { + boolean isInParsingMode() { return isInParsingMode; } - public void setSignatureFieldText(String text) { + void setSignatureFieldText(String text) { signatureFieldText = text; boolean signatureTextFieldInSync = signatureFieldText.equals(getFunctionSignatureTextFromModel()); @@ -1177,24 +1181,32 @@ public class FunctionEditorModel { } } - public void resetSignatureTextField() { + void resetSignatureTextField() { setSignatureFieldText(getFunctionSignatureTextFromModel()); } - public boolean hasChanges() { + boolean hasChanges() { return !Objects.equals(getFunctionSignatureTextFromModel(), signatureFieldText); } - public void parseSignatureFieldText() throws ParseException, CancelledException { + void parseSignatureFieldText() throws ParseException, CancelledException { FunctionSignatureParser parser = new FunctionSignatureParser(program.getDataTypeManager(), dataTypeManagerService); FunctionDefinitionDataType f = parser.parse(getFunctionSignature(), signatureFieldText); + // Preserve calling convention and noreturn flag from current model + f.setNoReturn(isNoReturn); + try { + f.setCallingConvention(callingConventionName); + } + catch (InvalidInputException e) { + // ignore + } setFunctionData(f); isInParsingMode = false; } - public int getFunctionNameStartPosition() { + int getFunctionNameStartPosition() { return returnInfo.getFormalDataType().getName().length() + 1; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java index 7bac625749..81a4413070 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/LanguageProviderPlugin.java @@ -221,7 +221,8 @@ public final class LanguageProviderPlugin extends Plugin implements ApplicationL Program program = (Program) dobj; monitor.setMessage("Identify Language..."); - SetLanguageDialog dialog = new SetLanguageDialog(tool, program); + SetLanguageDialog dialog = new SetLanguageDialog(tool, program, + "Set Language: " + program.getDomainFile().getName()); LanguageID langDescID = dialog.getLanguageDescriptionID(); CompilerSpecID compilerSpecDescID = dialog.getCompilerSpecDescriptionID(); if ((langDescID == null) || (compilerSpecDescID == null)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java index 0913d263a8..b7e1bd355c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/processors/SetLanguageDialog.java @@ -22,59 +22,81 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.plugin.importer.LcsSelectionListener; import ghidra.plugin.importer.NewLanguagePanel; import ghidra.program.model.lang.*; -import ghidra.program.model.listing.Program; import ghidra.program.util.DefaultLanguageService; import ghidra.util.HelpLocation; +import ghidra.util.Msg; public class SetLanguageDialog extends DialogComponentProvider { private NewLanguagePanel selectLangPanel; - private LanguageService langService; private PluginTool tool; - private Program currProgram; + private LanguageCompilerSpecPair currentLCSPair; - private LanguageID dialogLanguageDescID; - private CompilerSpecID dialogCompilerSpecDescID; + private LanguageID dialogLanguageID; + private CompilerSpecID dialogCompilerSpecID; LcsSelectionListener listener = e -> { LanguageID langID = null; CompilerSpecID compilerSpecID = null; - if (e.selection != null) { + if (e != null && e.selection != null) { langID = e.selection.languageID; compilerSpecID = e.selection.compilerSpecID; } - if ((langID != null) && (langID.equals(currProgram.getLanguageID()))) { - if ((compilerSpecID != null) && - (compilerSpecID.equals(currProgram.getCompilerSpec().getCompilerSpecID()))) { - //selectLangPanel.setNotificationText("Please select a different Language or Compiler Spec."); + if ((currentLCSPair != null) && (langID != null) && + (langID.equals(currentLCSPair.getLanguageID()))) { + if (compilerSpecID != null && + compilerSpecID.equals(currentLCSPair.getCompilerSpecID())) { setStatusText("Please select a different Language or Compiler Spec."); setOkEnabled(false); } else { - //selectLangPanel.setNotificationText(null); setStatusText(null); setOkEnabled(true); } return; } - //selectLangPanel.setNotificationText("Setting the language from '" + currProgram.getLanguageName() + "' to '" + langDesc.getName() + "'..."); - //selectLangPanel.setNotificationText(null); setStatusText(null); setOkEnabled(langID != null); }; - public SetLanguageDialog(PluginTool tool, Program program) { - super(getTitle(program), true, true, true, false); - currProgram = program; - this.tool = tool; + /** + * Construct set Language/Compiler-Spec dialog + * @param tool parent tool + * @param programArch current program architecture or null + * @param title dialog title + */ + public SetLanguageDialog(PluginTool tool, ProgramArchitecture programArch, String title) { + this(tool, programArch != null ? programArch.getLanguageCompilerSpecPair() : null, title); + } - langService = DefaultLanguageService.getLanguageService(); + /** + * Construct set Language/Compiler-Spec dialog + * @param tool parent tool + * @param languageId initial language ID or null + * @param compilerSpecId initial Compiler-Spec ID or null + * @param title dialog title + */ + public SetLanguageDialog(PluginTool tool, String languageId, String compilerSpecId, + String title) { + this(tool, getLanguageCompilerSpecPair(languageId, compilerSpecId), title); + } + + /** + * Construct set Language/Compiler-Spec dialog + * @param tool parent tool + * @param lcsPair language/compiler-spec ID pair or null + * @param title dialog title + */ + public SetLanguageDialog(PluginTool tool, LanguageCompilerSpecPair lcsPair, String title) { + super(title, true, true, true, false); + currentLCSPair = lcsPair; + this.tool = tool; selectLangPanel = new NewLanguagePanel(); - LanguageCompilerSpecPair lcsPair = new LanguageCompilerSpecPair(currProgram.getLanguageID(), - currProgram.getCompilerSpec().getCompilerSpecID()); - selectLangPanel.setSelectedLcsPair(lcsPair); + if (lcsPair != null) { + selectLangPanel.setSelectedLcsPair(lcsPair); + } selectLangPanel.addSelectionListener(listener); @@ -83,35 +105,57 @@ public class SetLanguageDialog extends DialogComponentProvider { addWorkPanel(selectLangPanel); addOKButton(); addCancelButton(); - //getComponent().setPreferredSize(new Dimension(450, 430)); + setOkEnabled(false); setHelpLocation(new HelpLocation("LanguageProviderPlugin", "set language")); selectLangPanel.setShowRecommendedCheckbox(false); + + listener.valueChanged(null); // kick to establish initial button enablement } - private static String getTitle(Program program) { - return "Set Language: " + program.getDomainFile().getName(); + private static LanguageCompilerSpecPair getLanguageCompilerSpecPair(String languageIdStr, + String compilerSpecIdStr) { + if (languageIdStr == null) { + return null; + } + LanguageService languageService = DefaultLanguageService.getLanguageService(); + try { + LanguageID languageId = new LanguageID(languageIdStr); + LanguageDescription descr = languageService.getLanguageDescription(languageId); + CompilerSpecID compilerSpecId = new CompilerSpecID(compilerSpecIdStr); + try { + descr.getCompilerSpecDescriptionByID(compilerSpecId); + } + catch (CompilerSpecNotFoundException e) { + Msg.warn(SetLanguageDialog.class, e.getMessage()); + } + return new LanguageCompilerSpecPair(languageId, compilerSpecId); + } + catch (LanguageNotFoundException e) { + Msg.warn(SetLanguageDialog.class, e.getMessage()); + return null; + } } - LanguageID getLanguageDescriptionID() { + public LanguageID getLanguageDescriptionID() { tool.showDialog(this); - return dialogLanguageDescID; + return dialogLanguageID; } - CompilerSpecID getCompilerSpecDescriptionID() { - return dialogCompilerSpecDescID; + public CompilerSpecID getCompilerSpecDescriptionID() { + return dialogCompilerSpecID; } @Override protected void okCallback() { LanguageCompilerSpecPair selectedLcsPair = selectLangPanel.getSelectedLcsPair(); if (selectedLcsPair == null) { - dialogLanguageDescID = null; - dialogCompilerSpecDescID = null; + dialogLanguageID = null; + dialogCompilerSpecID = null; } else { - dialogLanguageDescID = selectedLcsPair.languageID; - dialogCompilerSpecDescID = selectedLcsPair.compilerSpecID; + dialogLanguageID = selectedLcsPair.languageID; + dialogCompilerSpecID = selectedLcsPair.compilerSpecID; } close(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java index a9a33ca3ad..83ecf27cbe 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/DataTypeNamingUtil.java @@ -16,6 +16,8 @@ package ghidra.app.util; import ghidra.program.model.data.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.FunctionSignature; import ghidra.util.InvalidNameException; public class DataTypeNamingUtil { @@ -42,12 +44,16 @@ public class DataTypeNamingUtil { StringBuilder sb = new StringBuilder(ANONYMOUS_FUNCTION_DEF_PREFIX); - GenericCallingConvention convention = functionDefinition.getGenericCallingConvention(); - if (convention != null && convention != GenericCallingConvention.unknown) { - sb.append(convention.getDeclarationName()); + if (functionDefinition.hasNoReturn()) { + sb.append("_").append(FunctionSignature.NORETURN_DISPLAY_STRING); } - sb.append("_"); + String convention = functionDefinition.getCallingConventionName(); + if (convention != null && !Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(convention)) { + sb.append("_").append(convention); + } + + sb.append("_"); sb.append(mangleDTName(returnType.getName())); for (ParameterDefinition p : parameters) { sb.append("_").append(mangleDTName(p.getDataType().getName())); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java index efd670063a..9edf8b22e7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/ToolTipUtils.java @@ -289,12 +289,20 @@ public class ToolTipUtils { } buffy.append(friendlyEncodeHTML(function.getReturnType().getName())); buffy.append(HTML_SPACE); - PrototypeModel callingConvention = function.getCallingConvention(); - if (isNonDefaultCallingConvention(callingConvention)) { - buffy.append(friendlyEncodeHTML(callingConvention.getName())); + + String callingConvention = function.getCallingConventionName(); + if (callingConvention.equals(Function.DEFAULT_CALLING_CONVENTION_STRING)) { + callingConvention = function.getCallingConvention().getName(); + } + if (!callingConvention.equals(Function.UNKNOWN_CALLING_CONVENTION_STRING)) { + String ccHtml = friendlyEncodeHTML(callingConvention); + if (function.hasUnknownCallingConventionName()) { + ccHtml = colorString(Color.RED, ccHtml); + } + buffy.append(ccHtml); buffy.append(HTML_SPACE); } - + String functionName = StringUtilities.trimMiddle(function.getName(), LINE_LENGTH); buffy.append(colorString(FunctionColors.NAME, friendlyEncodeHTML(functionName))); buffy.append(HTML_SPACE).append("("); @@ -304,14 +312,6 @@ public class ToolTipUtils { return buffy.toString(); } - private static boolean isNonDefaultCallingConvention(PrototypeModel callingConvention) { - if (callingConvention == null) { - return false; - } - - return !Function.DEFAULT_CALLING_CONVENTION_STRING.equals(callingConvention.getName()); - } - private static void buildParameterPreview(Function function, StringBuilder buffy) { int rawTextLength = 0; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java index 73b55ff6d0..81b0d4bf1f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java @@ -16,13 +16,11 @@ package ghidra.app.util.bin.format.dwarf4.next; import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_base_type; -import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_subrange_type; - -import java.util.*; -import java.util.stream.Collectors; +import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*; import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -35,7 +33,9 @@ import ghidra.program.database.DatabaseObject; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; +import ghidra.program.model.lang.CompilerSpec; import ghidra.util.Msg; +import ghidra.util.exception.InvalidInputException; /** * Creates Ghidra {@link DataType}s using information from DWARF debug entries. The caller @@ -301,6 +301,7 @@ public class DWARFDataTypeImporter { FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager); funcDef.setReturnType(returnType.dataType); + funcDef.setNoReturn(diea.getBool(DW_AT_noreturn, false)); funcDef.setArguments(params.toArray(new ParameterDefinition[params.size()])); if (!diea.getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty()) { @@ -308,7 +309,12 @@ public class DWARFDataTypeImporter { } if (foundThisParam) { - funcDef.setGenericCallingConvention(GenericCallingConvention.thiscall); + try { + funcDef.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); + } + catch (InvalidInputException e) { + Msg.error(this, "Unexpected calling convention error", e); + } } if (dni.isAnon() && mangleAnonFuncNames) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java index 81dda81a50..b2437cf07c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java @@ -15,22 +15,22 @@ */ package ghidra.app.util.bin.format.dwarf4.next; +import java.io.IOException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.io.IOException; - import ghidra.app.util.bin.format.dwarf4.*; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; +import ghidra.app.util.bin.format.dwarf4.encoding.*; import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeImporter.DWARFDataType; import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.Swing; import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; import utility.function.Dummy; @@ -726,13 +726,19 @@ public class DWARFDataTypeManager { FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(dni.getParentCP(), dni.getName(), dataTypeManager); funcDef.setReturnType(returnDataType); + funcDef.setNoReturn(diea.getBool(DWARFAttribute.DW_AT_noreturn, false)); funcDef.setArguments(params.toArray(new ParameterDefinition[params.size()])); if (!diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty()) { funcDef.setVarArgs(true); } if (foundThisParam) { - funcDef.setGenericCallingConvention(GenericCallingConvention.thiscall); + try { + funcDef.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); + } + catch (InvalidInputException e) { + Msg.error(this, "Unexpected calling convention error", e); + } } return funcDef; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java index 62a054a6c1..0f9f1a5ffb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java @@ -388,7 +388,8 @@ public class DWARFFunctionImporter { Parameter curparam = buildParameter(gfunc, i, dfunc.params.get(i), diea); params.add(curparam); if (i == 0 && checkThisParameter(dfunc.params.get(0), diea)) { - convention = compilerSpec.matchConvention(GenericCallingConvention.thiscall); + convention = + compilerSpec.matchConvention(CompilerSpec.CALLING_CONVENTION_thiscall); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/FunctionDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/FunctionDataTypeHTMLRepresentation.java index c927613963..3050086820 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/FunctionDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/FunctionDataTypeHTMLRepresentation.java @@ -21,10 +21,12 @@ import java.awt.Color; import java.util.ArrayList; import java.util.List; +import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.app.util.ToolTipUtils; import ghidra.app.util.html.diff.DataTypeDiff; import ghidra.app.util.html.diff.DataTypeDiffBuilder; import ghidra.program.model.data.*; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.FunctionSignature; import ghidra.util.HTMLUtilities; import ghidra.util.StringUtilities; @@ -113,14 +115,26 @@ public class FunctionDataTypeHTMLRepresentation extends HTMLDataTypeRepresentati } private TextLine buildReturnType(FunctionDefinition functionDefinition) { + DataType returnDataType = functionDefinition.getReturnType(); - GenericCallingConvention genericCallingConvention = - functionDefinition.getGenericCallingConvention(); - String modifier = genericCallingConvention != GenericCallingConvention.unknown - ? (" " + genericCallingConvention.getDeclarationName()) - : ""; - return new TextLine( - HTMLUtilities.friendlyEncodeHTML(returnDataType.getDisplayName()) + modifier); + String rtHtml = friendlyEncodeHTML(returnDataType.getDisplayName()); + + String noReturnHtml = ""; + if (functionDefinition.hasNoReturn()) { + noReturnHtml = FunctionSignature.NORETURN_DISPLAY_STRING + HTML_SPACE; + } + + String ccHtml = ""; + String callingConvention = functionDefinition.getCallingConventionName(); + if (!callingConvention.equals(Function.UNKNOWN_CALLING_CONVENTION_STRING)) { + ccHtml = friendlyEncodeHTML(callingConvention); + if (functionDefinition.hasUnknownCallingConventionName()) { + ccHtml = colorString(Messages.ERROR, ccHtml); + } + ccHtml = HTML_SPACE + ccHtml; + } + + return new TextLine(noReturnHtml + rtHtml + ccHtml); } // display name to name pairs diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java index 77bda1c845..7c71990b49 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java @@ -123,12 +123,11 @@ public class FunctionSignatureFieldFactory extends FieldFactory { if (callingConvention.equals(Function.DEFAULT_CALLING_CONVENTION_STRING)) { callingConvention = function.getCallingConvention().getName(); } - if (callingConvention != null && - !callingConvention.equals(Function.UNKNOWN_CALLING_CONVENTION_STRING)) { + if (!callingConvention.equals(Function.UNKNOWN_CALLING_CONVENTION_STRING)) { as = new AttributedString(callingConvention + " ", FunctionColors.RETURN_TYPE, getMetrics()); textElements - .add(new FunctionCallingConventionFieldElement(as, elementIndex, 0, startCol)); + .add(new FunctionCallingConventionFieldElement(as, elementIndex, 0, startCol)); startCol += as.length(); elementIndex++; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LanguageSortedTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LanguageSortedTableModel.java index a8957c9c72..8db1e6c6ef 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LanguageSortedTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugin/importer/LanguageSortedTableModel.java @@ -129,6 +129,14 @@ public class LanguageSortedTableModel extends AbstractSortedTableModel + * NOTE: If archive has an assigned architecture, issues may arise due to a revised or + * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not + * prevent the archive from being opened. Such a warning condition will be logged and may + * result in missing or stale information for existing datatypes which have architecture related + * data. In some case it may be appropriate to + * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive + * object prior to its use. + * * @param archiveFile the archive file to open * @param readOnly should file be opened read only * @return the data type manager @@ -2495,8 +2506,7 @@ public class FlatProgramAPI { */ public final FileDataTypeManager openDataTypeArchive(File archiveFile, boolean readOnly) throws Exception { - FileDataTypeManager dtfm = FileDataTypeManager.openFileArchive(archiveFile, !readOnly); - return dtfm; + return FileDataTypeManager.openFileArchive(archiveFile, !readOnly); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java index cd86c27bff..23b353b7e1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/FunctionUtility.java @@ -20,7 +20,6 @@ import java.util.*; import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; -import ghidra.program.model.data.GenericCallingConvention; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; import ghidra.program.model.listing.*; @@ -64,7 +63,7 @@ public class FunctionUtility { Program sourceProgram = sourceFunction.getProgram(); Program destinationProgram = destinationFunction.getProgram(); - boolean sameLanguage = isSameLanguage(destinationProgram, sourceProgram); + boolean sameLanguage = isSameLanguageAndCompilerSpec(destinationProgram, sourceProgram); String callingConventionName = determineCallingConventionName(destinationFunction, sourceFunction, sameLanguage); @@ -274,7 +273,7 @@ public class FunctionUtility { * @param program2 the second program * @return true if the two programs have the same processor language and compiler spec. */ - public static boolean isSameLanguage(Program program1, Program program2) { + public static boolean isSameLanguageAndCompilerSpec(Program program1, Program program2) { Language language1 = program1.getLanguage(); Language language2 = program2.getLanguage(); if (language1.getLanguageID() != language2.getLanguageID()) { @@ -289,17 +288,9 @@ public class FunctionUtility { } private static String determineCallingConventionName(Function destinationFunction, - Function sourceFunction, boolean sameLanguage) { - String sourceCallingConventionName = sourceFunction.getCallingConventionName(); - if (sameLanguage) { - return sourceCallingConventionName; // Same language, so set to source. - } - GenericCallingConvention guessedCallingConvention = - GenericCallingConvention.guessFromName(sourceCallingConventionName); - if (guessedCallingConvention == GenericCallingConvention.thiscall) { - return GenericCallingConvention.thiscall.name(); // this call - } - return destinationFunction.getCallingConventionName(); // leave destination unchanged. + Function sourceFunction, boolean sameLanguageAndCompilerSpec) { + return sameLanguageAndCompilerSpec ? sourceFunction.getCallingConventionName() + : destinationFunction.getCallingConventionName(); } private static boolean determineCustomStorageUse(Function destinationFunction, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java index 5b0fd0606a..cd2f94abe1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java @@ -44,6 +44,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.disassemble.DisassemblerContextImpl; import ghidra.program.model.address.*; import ghidra.program.model.data.*; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.listing.Function.FunctionUpdateType; @@ -971,6 +972,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E ResourceFile emuTestingArchive = Application.getModuleDataFile("pcodetest/EmuTesting.gdt"); archiveDtMgr = FileDataTypeManager.openFileArchive(emuTestingArchive, false); + assertEquals(ArchiveWarning.NONE, archiveDtMgr.getWarning()); DataType dt = archiveDtMgr.getDataType(CategoryPath.ROOT, TEST_INFO_STRUCT_NAME); if (dt == null || !(dt instanceof Structure)) { fail(TEST_INFO_STRUCT_NAME + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/UndefinedFunction.java b/Ghidra/Features/Base/src/main/java/ghidra/util/UndefinedFunction.java index 08cffc06c7..6e65f5b5ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/UndefinedFunction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/UndefinedFunction.java @@ -218,6 +218,11 @@ public class UndefinedFunction implements Function { return p.getCompilerSpec().getDefaultCallingConvention(); } + @Override + public boolean hasUnknownCallingConventionName() { + return true; + } + @Override public String getCallingConventionName() { return Function.UNKNOWN_CALLING_CONVENTION_STRING; @@ -233,11 +238,6 @@ public class UndefinedFunction implements Function { return new String[0]; } - @Override - public String getDefaultCallingConventionName() { - return p.getCompilerSpec().getDefaultCallingConvention().getName(); - } - @Override public Address getEntryPoint() { return entry; diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index 5e6775e301..84628aee6b 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -625,8 +625,9 @@ public class CParser { private void applyFunctionQualifiers(Declaration dec, FunctionDefinitionDataType funcDT) { List qualifierList = dec.getQualifiers(); if (qualifierList.contains(NORETURN) ) { - // TODO: set no return on function + funcDT.setNoReturn(true); } + // TODO: switch to setting calling convention by string identifier for (Integer qualifier : qualifierList) { switch (qualifier) { case CDECL: diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java index ecaad2678d..c2fc0500db 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java @@ -45,17 +45,12 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeEditedInMy() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { // Make no changes to Latest. } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -99,9 +94,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeEditedInBoth() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -126,9 +119,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -171,9 +161,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeRenamedChanged() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -197,9 +185,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -235,9 +220,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeRenamedChanged2() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -263,9 +246,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -309,9 +289,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeRenamedChanged3() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -340,9 +318,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -383,9 +358,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeRenamedChanged4() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -411,9 +384,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -458,9 +428,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDataTypeRenamedInBoth() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -487,9 +455,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -532,9 +497,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testRenamedChanged() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -556,9 +519,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -602,9 +562,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { // in Latest move data type; in MY change the name; // should be a conflict mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -628,9 +586,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -666,9 +621,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testRenamedChangedMoved() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -699,9 +652,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -739,9 +689,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testRenamedChangedMovedNoConflict() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -772,9 +720,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -813,9 +758,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testRenamedChangedMovedNoConflict2() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -835,9 +778,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -889,9 +829,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { // edit DLL_Table in latest; edit DLL_Table in private // only DLL_Table should be in conflict; not the ones where it is used. mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -919,9 +857,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -963,9 +898,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { // edit DLL_Table in latest; edit DLL_Table in private // only DLL_Table should be in conflict; not the ones where it is used. mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -993,9 +926,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1037,9 +967,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { // edit ArrayStruct in latest; edit ArrayStruct in private // only ArrayStruct should be in conflict; not the ones where it is used. mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1067,9 +995,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1337,9 +1262,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testDeletedInLatest() throws Exception { mtf.initialize("notepad2", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1356,9 +1279,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1429,9 +1349,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testAddedFuncSig() throws Exception { mtf.initialize("notepad2", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1448,9 +1366,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1496,9 +1411,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testEditFuncSig() throws Exception { mtf.initialize("notepad3", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1517,9 +1430,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1562,9 +1472,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testEditFuncSig2() throws Exception { mtf.initialize("notepad3", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1585,9 +1493,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1629,9 +1534,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testEditFuncSig3() throws Exception { mtf.initialize("notepad3", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1642,6 +1545,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { try { fd.setVarArgs(true); + fd.setNoReturn(true); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); dtm.remove(foo, TaskMonitor.DUMMY); commit = true; @@ -1651,9 +1555,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1689,16 +1590,15 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { assertEquals(DataType.DEFAULT, vars[0].getDataType()); assertEquals("this is a comment", vars[0].getComment()); assertEquals(DataType.DEFAULT, vars[1].getDataType()); - assertEquals(false, fd.hasVarArgs()); + assertFalse(fd.hasVarArgs()); + assertFalse(fd.hasNoReturn()); } @Test public void testEditFuncSig4() throws Exception { mtf.initialize("notepad3", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1709,6 +1609,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { try { fd.setVarArgs(true); + fd.setNoReturn(true); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); dtm.remove(foo, TaskMonitor.DUMMY); commit = true; @@ -1718,9 +1619,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1759,16 +1657,15 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { checkDataType(new CharDataType(), vars[1].getDataType()); checkDataType(new Undefined4DataType(), vars[2].getDataType()); checkDataType(new Undefined4DataType(), vars[3].getDataType()); - assertEquals(true, fd.hasVarArgs()); + assertTrue(fd.hasVarArgs()); + assertTrue(fd.hasNoReturn()); } @Test public void testEditFuncSig5() throws Exception { mtf.initialize("notepad3", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1788,9 +1685,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1808,6 +1702,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { "this is another comment"); fd.setArguments(newVars); fd.setVarArgs(true); + fd.setNoReturn(true); commit = true; } finally { @@ -1831,6 +1726,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { assertEquals("Bar", vars[4].getName()); assertEquals("this is another comment", vars[4].getComment()); assertTrue(fd.hasVarArgs()); + assertTrue(fd.hasNoReturn()); } private void checkDataType(DataType expectedDataType, DataType actualDataType) { @@ -1842,9 +1738,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { public void testAddConflictFuncSig1() throws Exception { mtf.initialize("notepad3", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1866,9 +1760,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1902,7 +1793,7 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { assertEquals("format", vars[0].getName()); assertEquals(null, vars[0].getComment()); checkDataType(new WordDataType(), fd1.getReturnType()); - assertEquals(false, fd1.hasVarArgs()); + assertFalse(fd1.hasVarArgs()); FunctionDefinition fd2 = (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), "printf.conflict"); @@ -1913,7 +1804,80 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { assertEquals("format", vars2[0].getName()); assertEquals(null, vars2[0].getComment()); checkDataType(new WordDataType(), fd2.getReturnType()); - assertEquals(true, fd2.hasVarArgs()); + assertTrue(fd2.hasVarArgs()); + } + + @Test + public void testAddConflictFuncSig2() throws Exception { + + mtf.initialize("notepad3", new ProgramModifierListener() { + + @Override + public void modifyLatest(ProgramDB program) throws Exception { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + FunctionDefinition fd = + new FunctionDefinitionDataType(new CategoryPath("/MISC"), "exit"); + fd.setReturnType(VoidDataType.dataType); + fd.setNoReturn(false); + fd.setArguments( + new ParameterDefinition[] { new ParameterDefinitionImpl("rc", + IntegerDataType.dataType, null) }); + dtm.addDataType(fd, DataTypeConflictHandler.DEFAULT_HANDLER); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) throws Exception { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + FunctionDefinition fd = + new FunctionDefinitionDataType(new CategoryPath("/MISC"), "exit"); + fd.setReturnType(VoidDataType.dataType); + fd.setNoReturn(true); + fd.setArguments( + new ParameterDefinition[] { new ParameterDefinitionImpl("rc", + IntegerDataType.dataType, null) }); + dtm.addDataType(fd, DataTypeConflictHandler.DEFAULT_HANDLER); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + }); + executeMerge(DataTypeMergeManager.OPTION_MY); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + FunctionDefinition fd1 = + (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), "exit"); + assertNotNull(fd1); + ParameterDefinition[] vars = fd1.getArguments(); + assertEquals(1, vars.length); + checkDataType(IntegerDataType.dataType, vars[0].getDataType()); + assertEquals("rc", vars[0].getName()); + assertEquals(null, vars[0].getComment()); + checkDataType(VoidDataType.dataType, fd1.getReturnType()); + assertFalse(fd1.hasNoReturn()); + + FunctionDefinition fd2 = + (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), "exit.conflict"); + assertNotNull(fd2); + ParameterDefinition[] vars2 = fd2.getArguments(); + assertEquals(1, vars2.length); + checkDataType(IntegerDataType.dataType, vars2[0].getDataType()); + assertEquals("rc", vars2[0].getName()); + assertEquals(null, vars2[0].getComment()); + checkDataType(VoidDataType.dataType, fd2.getReturnType()); + assertTrue(fd2.hasNoReturn()); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java index 35eb92c428..0c31124a99 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java @@ -37,10 +37,13 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.data.*; +import ghidra.program.model.data.StandAloneDataTypeManager.LanguageUpdateOption; +import ghidra.program.model.lang.*; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; import ghidra.util.InvalidNameException; import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedIntegrationTest { @@ -93,6 +96,45 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte waitForTree(); } + protected DataType resolveDataType(StandAloneDataTypeManager archiveDtm, DataType dt) + throws Exception { + int id = archiveDtm.startTransaction("resolve datatype"); + try { + return archiveDtm.resolve(dt, null); + } + finally { + archiveDtm.endTransaction(id, true); + waitForTree(); + } + } + + protected void setArchitecture(StandAloneDataTypeManager archiveDtm, String languageId, + String compilerSpecId) throws Exception { + LanguageService languageService = getLanguageService(); + Language language = languageService.getLanguage(new LanguageID(languageId)); + + int id = archiveDtm.startTransaction("set architecture"); + try { + archiveDtm.setProgramArchitecture(language, new CompilerSpecID(compilerSpecId), + LanguageUpdateOption.CLEAR, TaskMonitor.DUMMY); + } + finally { + archiveDtm.endTransaction(id, true); + } + waitForTree(); + } + + protected void removeArchitecture(StandAloneDataTypeManager archiveDtm) throws Exception { + int id = archiveDtm.startTransaction("remove architecture"); + try { + archiveDtm.clearProgramArchitecture(TaskMonitor.DUMMY); + } + finally { + archiveDtm.endTransaction(id, true); + } + waitForTree(); + } + protected void createNewArchive(String archiveName, boolean deleteExisting) throws Exception { File archiveFile = new File(getTestDirectoryPath(), archiveName); if (deleteExisting && archiveFile.exists()) { @@ -190,10 +232,12 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte @After public void tearDown() throws Exception { - SwingUtilities.invokeLater(() -> { - ProgramManager pm = tool.getService(ProgramManager.class); - pm.closeProgram(); - }); + if (tool != null) { + SwingUtilities.invokeLater(() -> { + ProgramManager pm = tool.getService(ProgramManager.class); + pm.closeProgram(); + }); + } waitForPostedSwingRunnables(); // this handles the save changes dialog and potential analysis dialogs diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java index cfef7d4fe3..fcfe469f5b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateArchive1Test.java @@ -32,7 +32,9 @@ import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.archive.FileArchive; import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; import ghidra.framework.GenericRunInfo; -import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.ProgramArchitecture; import ghidra.util.Msg; public class CreateArchive1Test extends AbstractCreateArchiveTest { @@ -62,6 +64,10 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { createNewArchive(string + FileDataTypeManager.SUFFIX, true); ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + StandAloneDataTypeManager dtm = + (StandAloneDataTypeManager) archiveNode.getCategory().getDataTypeManager(); + assertNull(dtm.getProgramArchitecture()); + createCategory(archiveNode.getCategory(), "bob"); waitForTree(); @@ -79,6 +85,9 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { DataTypeTestUtils.performAction(action, tree); waitForTree(); + archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + assertNull(archiveNode); + archiveNode = DataTypeTestUtils.openArchive(getTestDirectoryPath(), "MyArchive.gdt", false, plugin); assertNotNull(archiveNode.getChild("bob")); @@ -88,6 +97,65 @@ public class CreateArchive1Test extends AbstractCreateArchiveTest { f.deleteOnExit(); } + @Test + public void testCreateAndPopulateWithArchitecture() throws Exception { + + String string = "MyArchive"; + createNewArchive(string + FileDataTypeManager.SUFFIX, true); + + ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + StandAloneDataTypeManager dtm = + (StandAloneDataTypeManager) archiveNode.getCategory().getDataTypeManager(); + assertNull(dtm.getProgramArchitecture()); + setArchitecture(dtm, ProgramBuilder._TOY64_LE, "default"); + + assertEquals(8, dtm.getPointer(DataType.DEFAULT).getLength()); + + createCategory(archiveNode.getCategory(), "bob"); + waitForTree(); + + DataType dt = + resolveDataType(dtm, new StructureDataType(new CategoryPath("/bob"), "MyStruct", 0)); + dt = resolveDataType(dtm, new PointerDataType(dt)); + assertEquals(8, dtm.getPointer(DataType.DEFAULT).getLength()); + DataTypePath dataTypePath = dt.getDataTypePath(); + + tree.setSelectedNode(archiveNode); + waitForTree(); + + createCategory(archiveNode.getCategory(), "joe"); + waitForTree(); + + DockingActionIf action = getAction(plugin, "Save"); + DataTypeTestUtils.performAction(action, tree); + waitForTree(); + + action = getAction(plugin, "Close Archive"); + DataTypeTestUtils.performAction(action, tree); + waitForTree(); + + archiveNode = (ArchiveNode) archiveRootNode.getChild("MyArchive"); + assertNull(archiveNode); + + archiveNode = + DataTypeTestUtils.openArchive(getTestDirectoryPath(), "MyArchive.gdt", false, plugin); + assertNotNull(archiveNode.getChild("bob")); + assertNotNull(archiveNode.getChild("joe")); + + dtm = (StandAloneDataTypeManager) archiveNode.getCategory().getDataTypeManager(); + ProgramArchitecture arch = dtm.getProgramArchitecture(); + assertNotNull("Expected architecture to be set", arch); + assertEquals(ProgramBuilder._TOY64_LE, arch.getLanguage().getLanguageID().toString()); + assertEquals("default", arch.getCompilerSpec().getCompilerSpecID().toString()); + + dt = dtm.getDataType(dataTypePath); + assertNotNull(dt); + assertEquals(8, dtm.getPointer(DataType.DEFAULT).getLength()); + + File f = new File(getTestDirectoryPath(), "MyArchive.gdt.bak"); + f.deleteOnExit(); + } + @Test public void testCreateArchiveNameCollision1() throws Exception { // create archive diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java index d8176ec876..31f0436ce3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java @@ -27,6 +27,7 @@ import generic.jar.ResourceFile; import generic.test.AbstractGenericTest; import ghidra.framework.Application; import ghidra.program.model.data.*; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; public class DataTypeArchiveIDTest extends AbstractGenericTest { @@ -68,6 +69,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { notFound.remove(path); FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); + assertEquals(ArchiveWarning.NONE, dtm.getWarning()); try { assertEquals("Archive UniversalID mismatch: " + path, oldID, dtm.getUniversalID().toString()); @@ -101,6 +103,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { public void spotCheckWindowsVS12_32() throws IOException { ResourceFile gdtFile = Application.getModuleDataFile(WIN_VS12_32_GDT_PATH); FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); + assertEquals(ArchiveWarning.NONE, dtm.getWarning()); try { DataType dt = dtm.getDataType("/winsock.h/fd_set"); assertNotNull(dt); @@ -117,6 +120,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { public void spotCheckWindowsVS12_64() throws IOException { ResourceFile gdtFile = Application.getModuleDataFile(WIN_VS12_64_GDT_PATH); FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); + assertEquals(ArchiveWarning.NONE, dtm.getWarning()); try { DataType dt = dtm.getDataType("/winsock.h/fd_set"); assertNotNull(dt); @@ -132,6 +136,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { public void spotCheckGenericCLib32() throws IOException { ResourceFile gdtFile = Application.getModuleDataFile(GENERIC_CLIB_32_GDT_PATH); FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); + assertEquals(ArchiveWarning.NONE, dtm.getWarning()); try { DataType dt = dtm.getDataType("/select.h/fd_set"); assertNotNull(dt); @@ -147,6 +152,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { public void spotCheckGenericCLib64() throws IOException { ResourceFile gdtFile = Application.getModuleDataFile(GENERIC_CLIB_64_GDT_PATH); FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); + assertEquals(ArchiveWarning.NONE, dtm.getWarning()); try { DataType dt = dtm.getDataType("/select.h/fd_set"); assertNotNull(dt); @@ -162,6 +168,7 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { public void spotCheckMacOS10_9() throws IOException { ResourceFile gdtFile = Application.getModuleDataFile(MAC_OS_10_9_GDT_PATH); FileDataTypeManager dtm = FileDataTypeManager.openFileArchive(gdtFile, false); + assertEquals(ArchiveWarning.NONE, dtm.getWarning()); try { DataType dt = dtm.getDataType("/_fd_def.h/fd_set"); assertNotNull(dt); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialogTest.java index 526b1edd86..f9f6fda857 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/function/EditFunctionSignatureDialogTest.java @@ -25,6 +25,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.model.FunctionTestDouble; import ghidra.program.model.TestDoubleFunctionSignature; import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.test.*; @@ -118,11 +119,9 @@ public class EditFunctionSignatureDialogTest extends AbstractGhidraHeadedIntegra //================================================================================================== private class LocalFunctionStub extends FunctionTestDouble { - private DataType returnType; public LocalFunctionStub(String name, String signature) { super("Name", new LocalFunctionSignatureTestDouble(name, signature)); - this.returnType = returnType; } @Override @@ -132,7 +131,7 @@ public class EditFunctionSignatureDialogTest extends AbstractGhidraHeadedIntegra @Override public String getCallingConventionName() { - return GenericCallingConvention.stdcall.toString(); + return CompilerSpec.CALLING_CONVENTION_stdcall; } @Override diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java index a8102ca6a6..66923c69a8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java @@ -1113,6 +1113,11 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration SourceArchive sourceArchive) { // don't care for now } + + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + // don't care for now + } } private class CustomDataType extends StructureDataType { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java index b1e40c20fd..09b223ad9d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/CategoryTest.java @@ -908,5 +908,10 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest { SourceArchive dataTypeSource) { // don't care } + + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + // don't care + } } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/InstanceSettingsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/InstanceSettingsTest.java new file mode 100644 index 0000000000..6dd937cccf --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/InstanceSettingsTest.java @@ -0,0 +1,265 @@ +/* ### + * 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.docking.settings.FormatSettingsDefinition; +import ghidra.docking.settings.Settings; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.data.*; +import ghidra.program.model.data.DataUtilities.ClearDataMode; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Listing; +import ghidra.program.model.mem.Memory; +import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * + * Test setting default and instance settings on Data. + * + * + */ +public class InstanceSettingsTest extends AbstractGhidraHeadedIntegrationTest { + + // Suitable settings allowed for StringDataType data + private static String LONG_SETTING_NAME = "mutability"; + private static String STRING_SETTING_NAME = "charset"; + + private ProgramDB program; + private ProgramDataTypeManager dataMgr; + private Listing listing; + private AddressSpace space; + private int transactionID; + + public InstanceSettingsTest() { + super(); + } + + @Before + public void setUp() throws Exception { + program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); + space = program.getAddressFactory().getDefaultAddressSpace(); + dataMgr = program.getDataTypeManager(); + listing = program.getListing(); + transactionID = program.startTransaction("Test"); + addBlock(); + + for (int i = 0; i < 40; i++) { + DataUtilities.createData(program, addr(i), StringDataType.dataType, 1, false, + ClearDataMode.CLEAR_ALL_CONFLICT_DATA); + } + } + + @After + public void tearDown() throws Exception { + if (program.getCurrentTransactionInfo() != null) { + program.endTransaction(transactionID, true); + } + program.release(this); + } + + @Test + public void testInstanceSettings() throws Exception { + + Data data = DataUtilities.createData(program, addr(10), ByteDataType.dataType, 1, false, + ClearDataMode.CLEAR_ALL_CONFLICT_DATA); + + DataType dt = data.getDataType(); + Settings defaultSettings = dt.getDefaultSettings(); + FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR); + EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false); + PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true); + + assertEquals(FormatSettingsDefinition.CHAR, data.getLong("format").longValue()); + FormatSettingsDefinition.DEF.setChoice(data, FormatSettingsDefinition.DECIMAL); + assertEquals(FormatSettingsDefinition.DECIMAL, data.getLong("format").longValue()); + + assertEquals(EndianSettingsDefinition.LITTLE, data.getLong("endian").longValue()); + EndianSettingsDefinition.DEF.setChoice(data, EndianSettingsDefinition.BIG); + assertEquals(EndianSettingsDefinition.BIG, data.getLong("endian").longValue()); + + assertEquals(PaddingSettingsDefinition.PADDED_VALUE, data.getLong("padded").longValue()); + PaddingSettingsDefinition.DEF.setChoice(data, PaddingSettingsDefinition.UNPADDED_VALUE); + assertEquals(PaddingSettingsDefinition.UNPADDED_VALUE, data.getLong("padded").longValue()); + + FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.HEX); + EndianSettingsDefinition.DEF.clear(defaultSettings); + PaddingSettingsDefinition.DEF.clear(defaultSettings); + + assertEquals(FormatSettingsDefinition.DECIMAL, data.getLong("format").longValue()); + assertEquals(EndianSettingsDefinition.BIG, data.getLong("endian").longValue()); + assertEquals(PaddingSettingsDefinition.UNPADDED_VALUE, data.getLong("padded").longValue()); + } + + @Test + public void testGetInstanceNames() throws Exception { + Data data = listing.getDataAt(addr(10)); + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); + + String[] names = data.getNames(); + assertEquals(2, names.length); + } + + @Test + public void testClearInstanceSettings() throws Exception { + Data data = listing.getDataAt(addr(10)); + + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); + + data.clearSetting(STRING_SETTING_NAME); + assertNull(data.getString(STRING_SETTING_NAME)); + } + + @Test + public void testClearAllInstanceSettings() throws Exception { + Data data = listing.getDataAt(addr(10)); + + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); + + data.clearAllSettings(); + assertNull(data.getString(STRING_SETTING_NAME)); + assertNull(data.getLong(LONG_SETTING_NAME)); + } + + @Test + public void testIsEmptyInstanceSettings() throws Exception { + Data data = listing.getDataAt(addr(10)); + + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); + + assertTrue(!data.isEmpty()); + data.clearAllSettings(); + + assertTrue(data.isEmpty()); + } + + @Test + public void testGetNames() throws Exception { + + Data data = listing.getDataAt(addr(10)); + + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); + + String[] names = data.getNames(); + assertEquals(2, names.length); + } + + private Data getDataAt(long offset) { + Data data = listing.getDataAt(addr(offset)); + assertNotNull("expected data at address 0x" + Long.toHexString(offset)); + return data; + } + + @Test + public void testMoveSettings() throws Exception { + + for (int i = 0; i < 10; i++) { + Data d = getDataAt(i); + dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i); + } + dataMgr.moveAddressRange(addr(0), addr(20), 10, TaskMonitor.DUMMY); + int j = 0; + for (int i = 20; i < 30; i++, j++) { + Data d = getDataAt(i); + + String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME); + assertEquals("red" + j, s); + + Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME); + assertEquals(j, lvalue.longValue()); + } + } + + @Test + public void testMoveSettings2() { + + for (int i = 0; i < 10; i++) { + Data d = getDataAt(i); + dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i); + } + try { + dataMgr.moveAddressRange(addr(0), addr(5), 10, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + Assert.fail("Unexpected cancelled exception"); + } + + int j = 0; + for (int i = 5; i < 15; i++, j++) { + Data d = getDataAt(i); + + String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME); + assertEquals("red" + j, s); + + Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME); + assertEquals(j, lvalue.longValue()); + } + } + + @Test + public void testMoveSettings3() { + + int j = 20; + for (int i = 20; i < 30; i++, j++) { + Data d = getDataAt(i); + dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i); + } + j = 20; + try { + dataMgr.moveAddressRange(addr(20), addr(5), 10, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + Assert.fail("Unexpected cancelled exception"); + } + for (int i = 5; i < 15; i++, j++) { + Data d = getDataAt(i); + + String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME); + assertEquals("red" + j, s); + + Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME); + assertEquals(j, lvalue.longValue()); + } + + } + + private Address addr(long l) { + return space.getAddress(l); + } + + private void addBlock() throws Exception { + + Memory memory = program.getMemory(); + memory.createInitializedBlock("test", addr(0), 100, (byte) 0, + TaskMonitor.DUMMY, false); + } +} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java index bd3aa5e7b0..39dc6c95d7 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java @@ -42,8 +42,8 @@ import ghidra.util.task.TaskMonitor; */ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { private ProgramDB program; - private ProgramBasedDataTypeManager dataMgr; private Listing listing; + private ProgramBasedDataTypeManager dataMgr; private AddressSpace space; private int transactionID; @@ -62,11 +62,12 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Before public void setUp() throws Exception { program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); + listing = program.getListing(); space = program.getAddressFactory().getDefaultAddressSpace(); dataMgr = program.getDataTypeManager(); - listing = program.getListing(); transactionID = program.startTransaction("Test"); addBlock(); + // pointer-typedef has the largest // System.out.println("Defined string settings:"); // for (SettingsDefinition def : StringDataType.dataType.getSettingsDefinitions()) { @@ -164,8 +165,8 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testIsEmpty() throws Exception { - Data data = listing.getDataAt(addr(10)); - Settings defaultSettings = data.getDataType().getDefaultSettings(); + DataType dt = dataMgr.resolve(StringDataType.dataType, null); + Settings defaultSettings = dt.getDefaultSettings(); defaultSettings.setString(STRING_SETTING_NAME, "red"); defaultSettings.setLong(LONG_SETTING_NAME, 10); @@ -390,7 +391,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testDefaultSettingsOnTypedef() throws Exception { - DataType byteDT = dataMgr.resolve(new ByteDataType(), null); + DataType byteDT = dataMgr.resolve(ByteDataType.dataType, null); SettingsDefinition[] settingsDefinitions = byteDT.getSettingsDefinitions(); Settings settings = byteDT.getDefaultSettings(); FormatSettingsDefinition.DEF.setChoice(settings, FormatSettingsDefinition.OCTAL); @@ -418,7 +419,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testDefaultSettingsOnTypedef2() throws Exception { - DataType byteDT = dataMgr.resolve(new ByteDataType(), null); + DataType byteDT = dataMgr.resolve(ByteDataType.dataType, null); Settings settings = byteDT.getDefaultSettings(); TypedefDataType tdt = new TypedefDataType("ByteTypedef", byteDT); @@ -438,7 +439,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testDefaultSettingsOnTypedefUndoRedo() throws Exception { - DataType byteDT = dataMgr.resolve(new ByteDataType(), null); + DataType byteDT = dataMgr.resolve(ByteDataType.dataType, null); Settings settings = byteDT.getDefaultSettings(); settings.setLong("format", FormatSettingsDefinition.OCTAL); endTransaction(); @@ -466,7 +467,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testDefaultSettingsOnDeletedTypdef() throws Exception { - DataType byteDT = dataMgr.resolve(new ByteDataType(), null); + DataType byteDT = dataMgr.resolve(ByteDataType.dataType, null); Settings settings = byteDT.getDefaultSettings(); settings.setLong("format", FormatSettingsDefinition.OCTAL); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionManagerTest.java index af88484ae9..f3cec3642f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/function/FunctionManagerTest.java @@ -17,8 +17,8 @@ package ghidra.program.database.function; import static org.junit.Assert.*; +import java.util.Collection; import java.util.Iterator; -import java.util.List; import org.junit.*; @@ -453,25 +453,12 @@ public class FunctionManagerTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(defaultModel, protoModel); } - @Test - public void testGetCallingConventions() throws Exception { - PrototypeModel[] protoModels = functionManager.getCallingConventions(); - assertTrue(protoModels.length >= 1); - } - @Test public void testGetCallingConventionNames() throws Exception { - - List names = functionManager.getCallingConventionNames(); + Collection names = functionManager.getCallingConventionNames(); assertTrue(names.size() >= 1); - for (String name : names) { - if (Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(name)) { - assertNull(functionManager.getCallingConvention(name)); - } - else { - assertNotNull(functionManager.getCallingConvention(name)); - } + assertNotNull(functionManager.getCallingConvention(name)); } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java index 3c9f2bd335..a06e8235d6 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/CParserTest.java @@ -15,10 +15,7 @@ */ package ghidra.app.util.cparser; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.InputStream; import java.util.ArrayList; @@ -28,30 +25,9 @@ import org.junit.Test; import ghidra.app.util.cparser.C.CParser; import ghidra.app.util.cparser.C.ParseException; -import ghidra.program.model.data.Array; -import ghidra.program.model.data.BuiltInDataType; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.CharDataType; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.DataTypeComponent; -import ghidra.program.model.data.DataTypeConflictHandler; -import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; -import ghidra.program.model.data.FunctionDefinition; -import ghidra.program.model.data.GenericCallingConvention; -import ghidra.program.model.data.LongLongDataType; -import ghidra.program.model.data.ParameterDefinition; -import ghidra.program.model.data.Pointer; -import ghidra.program.model.data.ShortDataType; -import ghidra.program.model.data.StandAloneDataTypeManager; -import ghidra.program.model.data.Structure; -import ghidra.program.model.data.StructureDataType; -import ghidra.program.model.data.TypeDef; -import ghidra.program.model.data.TypedefDataType; -import ghidra.program.model.data.UnsignedLongDataType; -import ghidra.program.model.data.UnsignedLongLongDataType; -import ghidra.program.model.data.UnsignedShortDataType; -import ghidra.program.model.data.WideCharDataType; +import ghidra.program.model.lang.CompilerSpec; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { @@ -285,14 +261,14 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { funcDef = (FunctionDefinition) dt; str = funcDef.getPrototypeString(); assertEquals("signature not correct", "void _Once(_Once_t * , _func_anon_ * )", replaceAnonFuncName(str)); - assertEquals("calling convention _Once", "__cdecl", funcDef.getGenericCallingConvention().getDeclarationName()); + assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); funcArgs = funcDef.getArguments(); assertTrue("struct fstruct", funcArgs[0].getDataType() instanceof Pointer); assertTrue("ptr", funcArgs[1].getDataType() instanceof Pointer); pointedToDT = ((Pointer) funcArgs[1].getDataType()).getDataType(); assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); funcDef = (FunctionDefinition) pointedToDT; - assertEquals("calling convention _Once", "__cdecl", funcDef.getGenericCallingConvention().getDeclarationName()); + assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); str = funcDef.getPrototypeString(); assertEquals("signature not correct", "void _func_anon_(void)", replaceAnonFuncName(str)); @@ -301,12 +277,12 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue("_Twice function definition", dt instanceof FunctionDefinition); funcDef = (FunctionDefinition) dt; str = funcDef.getPrototypeString(); - assertEquals("calling convention _Twice", "__stdcall", funcDef.getGenericCallingConvention().getDeclarationName()); + assertEquals("calling convention _Twice", "__stdcall", funcDef.getCallingConventionName()); funcArgs = funcDef.getArguments(); pointedToDT = ((Pointer) funcArgs[0].getDataType()).getDataType(); assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); funcDef = (FunctionDefinition) pointedToDT; - assertEquals("calling convention _Once", "__cdecl", funcDef.getGenericCallingConvention().getDeclarationName()); + assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); str = funcDef.getPrototypeString(); assertEquals("signature not correct", "void _func_anon_(void)", replaceAnonFuncName(str)); @@ -315,12 +291,12 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { assertTrue("_Twice function definition", dt instanceof FunctionDefinition); funcDef = (FunctionDefinition) dt; str = funcDef.getPrototypeString(); - assertEquals("calling convention _Thrice", "", funcDef.getGenericCallingConvention().getDeclarationName()); + assertEquals("calling convention _Thrice", "unknown", funcDef.getCallingConventionName()); funcArgs = funcDef.getArguments(); pointedToDT = ((Pointer) funcArgs[0].getDataType()).getDataType(); assertTrue("ptr not to a function", pointedToDT instanceof FunctionDefinition); funcDef = (FunctionDefinition) pointedToDT; - assertEquals("calling convention _Once", "__cdecl", funcDef.getGenericCallingConvention().getDeclarationName()); + assertEquals("calling convention _Once", "__cdecl", funcDef.getCallingConventionName()); str = funcDef.getPrototypeString(); assertEquals("signature not correct", "void _func_anon_(void)", replaceAnonFuncName(str)); @@ -368,21 +344,25 @@ public class CParserTest extends AbstractGhidraHeadlessIntegrationTest { dt = dtMgr.getDataType(new CategoryPath("/functions"), "stdcall_func"); assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); - assertTrue("Callee should not purge", ((FunctionDefinition) dt) - .getGenericCallingConvention() == GenericCallingConvention.stdcall); + assertTrue("Callee should not purge", CompilerSpec.CALLING_CONVENTION_stdcall + .equals(((FunctionDefinition) dt).getCallingConventionName())); assertTrue("signature not correct", str.equals("int stdcall_func(int b)")); dt = dtMgr.getDataType(new CategoryPath("/functions"), "cdecl_func"); assertTrue("not a function", dt instanceof FunctionDefinition); str = ((FunctionDefinition) dt).getPrototypeString(); - assertTrue("Caller should purge", ((FunctionDefinition) dt) - .getGenericCallingConvention() != GenericCallingConvention.stdcall); + assertTrue("Caller should purge", CompilerSpec.CALLING_CONVENTION_cdecl + .equals(((FunctionDefinition) dt).getCallingConventionName())); assertTrue("signature not correct", str.equals("int cdecl_func(int a)")); dt = dtMgr.getDataType(new CategoryPath("/functions"), "cdecl_func_after"); assertTrue("not a function", dt instanceof FunctionDefinition); - assertTrue("Caller should purge", ((FunctionDefinition) dt) - .getGenericCallingConvention() != GenericCallingConvention.stdcall); + assertTrue("Caller should purge", CompilerSpec.CALLING_CONVENTION_cdecl + .equals(((FunctionDefinition) dt).getCallingConventionName())); + + dt = dtMgr.getDataType(new CategoryPath("/functions"), "_Noreturn_exit"); + assertTrue("not a function", dt instanceof FunctionDefinition); + assertTrue("Caller should purge", ((FunctionDefinition) dt).hasNoReturn()); dt = dtMgr.getDataType(new CategoryPath("/"), "UINT2"); assertTrue(dt instanceof TypeDef); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java index 8fab182152..87255cf3cf 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/cparser/PreProcessorTest.java @@ -45,8 +45,11 @@ public class PreProcessorTest extends AbstractGenericTest { super(); } - @BeforeClass - public static void init() { + @Before + public void init() { + if (dtMgr != null) { + return; // do only once - but not too soon + } URL url = PreProcessorTest.class.getResource(resourceName); String[] args = new String[] { "-I" + url.getPath() + "/..", "-DFROM_ARG_VALUE=300", diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java index f203f1e15a..5939c6b36b 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeTest.java @@ -20,13 +20,13 @@ import static org.junit.Assert.*; import java.io.*; import java.util.HashMap; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; import ghidra.app.util.cparser.C.CParser; import ghidra.app.util.cparser.C.ParseException; import ghidra.util.Msg; import resources.ResourceManager; -abstract class AbstractCompositeTest extends AbstractGTest { +abstract class AbstractCompositeTest extends AbstractGenericTest { private HashMap copyMap = new HashMap<>(); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java index 5a60944385..b6a2032a1e 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java @@ -22,12 +22,12 @@ import java.util.List; import org.apache.commons.compress.utils.Sets; import org.junit.*; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; /** * */ -public class StructureDataTypeTest extends AbstractGTest { +public class StructureDataTypeTest extends AbstractGenericTest { private Structure struct; diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java index ddfbe019c8..e31e986100 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionDataTypeTest.java @@ -21,12 +21,12 @@ import org.junit.*; import com.google.common.collect.Sets; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; /** * */ -public class UnionDataTypeTest extends AbstractGTest { +public class UnionDataTypeTest extends AbstractGenericTest { private Union union; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index d513aacc11..1ab51f942c 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -8187,7 +8187,7 @@ public class RecoveredClassHelper { if ((DataTypeUtilities.equalsIgnoreConflict(signature.getName(), definition.getName())) && DataTypeUtilities.isSameOrEquivalentDataType(definition.getReturnType(), signature.getReturnType()) && - (definition.getGenericCallingConvention() == signature.getGenericCallingConvention()) && + signature.getCallingConventionName().equals(definition.getCallingConventionName()) && (definition.hasVarArgs() == signature.hasVarArgs())) { ParameterDefinition[] sigargs = signature.getArguments(); ParameterDefinition[] defArgs = definition.getArguments(); diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java index 1c5ef22bb2..b8d5483354 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/DeletePrototypeOverrideAction.java @@ -20,12 +20,14 @@ import ghidra.app.decompiler.ClangToken; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.util.HelpTopics; import ghidra.program.database.symbol.CodeSymbol; +import ghidra.program.model.data.ProgramBasedDataTypeManager; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.symbol.*; -import ghidra.util.*; +import ghidra.util.HelpLocation; +import ghidra.util.UndefinedFunction; public class DeletePrototypeOverrideAction extends AbstractDecompilerAction { @@ -84,14 +86,13 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction { CodeSymbol sym = getSymbol(func, context.getTokenAtCursor()); Program program = func.getProgram(); SymbolTable symtab = program.getSymbolTable(); - int transaction = program.startTransaction("Remove Override Signature"); - boolean commit = true; - if (!symtab.removeSymbolSpecial(sym)) { - commit = false; - Msg.showError(getClass(), context.getDecompilerPanel(), - "Removing Override Signature Failed", "Error removing override signature"); + ProgramBasedDataTypeManager dtm = program.getDataTypeManager(); + int txId = program.startTransaction("Remove Override Signature"); + try { + symtab.removeSymbolSpecial(sym); + } + finally { + program.endTransaction(txId, true); } - program.endTransaction(transaction, commit); - } } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java index 2c22a7f3e3..826133f6f9 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/OverridePrototypeAction.java @@ -32,6 +32,7 @@ import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.SourceType; import ghidra.util.*; import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; public class OverridePrototypeAction extends AbstractDecompilerAction { @@ -220,7 +221,13 @@ public class OverridePrototypeAction extends AbstractDecompilerAction { } PcodeOp callOp = getCallOp(context.getProgram(), context.getTokenAtCursor()); - return callOp != null; + if (callOp == null) { + return false; + } + + // don't enable if override already in place + return DeletePrototypeOverrideAction.getSymbol(context.getFunction(), + context.getTokenAtCursor()) == null; } @Override @@ -295,7 +302,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction { * @param conv initial calling convention */ public ProtoOverrideDialog(PluginTool tool, Function func, String signature, String conv) { - super(tool, "Override Signature", func, false, false, false); + super(tool, "Override Signature", func, false, true, false); setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionOverrideSignature")); this.initialSignature = signature; this.initialConvention = conv; @@ -331,9 +338,15 @@ public class OverridePrototypeAction extends AbstractDecompilerAction { return false; } - GenericCallingConvention convention = - GenericCallingConvention.guessFromName(getCallingConvention()); - functionDefinition.setGenericCallingConvention(convention); + functionDefinition.setNoReturn(hasNoReturnSelected()); + + try { + functionDefinition.setCallingConvention(getCallingConvention()); + } + catch (InvalidInputException e) { + // should not occur since dialog restricts calling convention choice + } + return true; } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/SpecifyCPrototypeAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/SpecifyCPrototypeAction.java index d825ac8516..294f5ed8d1 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/SpecifyCPrototypeAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/SpecifyCPrototypeAction.java @@ -47,6 +47,8 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction { */ private void verifyDynamicEditorModel(HighFunction hf, FunctionEditorModel model) { + // TODO: devise alternative approach - bad practice to manipulate model in this fashion + FunctionPrototype functionPrototype = hf.getFunctionPrototype(); int decompParamCnt = functionPrototype.getNumParams(); @@ -125,6 +127,7 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction { } fsig.setArguments(args); fsig.setVarArgs(functionPrototype.isVarArg()); + fsig.setNoReturn(functionPrototype.hasNoReturn()); return fsig; } diff --git a/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java b/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java index a2ce29af66..aff7b2564c 100644 --- a/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java +++ b/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/string/variadic/FormatStringParserTest.java @@ -23,8 +23,7 @@ import org.junit.Before; import org.junit.Test; import generic.test.AbstractGenericTest; -import ghidra.program.database.ProgramBuilder; -import ghidra.program.database.ProgramDB; +import ghidra.program.database.*; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.data.*; diff --git a/Ghidra/Features/FileFormats/ghidra_scripts/UpgradeDexToGhidra71Script.java b/Ghidra/Features/FileFormats/ghidra_scripts/UpgradeDexToGhidra71Script.java index 39aa3e33ba..121c255e2b 100644 --- a/Ghidra/Features/FileFormats/ghidra_scripts/UpgradeDexToGhidra71Script.java +++ b/Ghidra/Features/FileFormats/ghidra_scripts/UpgradeDexToGhidra71Script.java @@ -24,9 +24,7 @@ import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.FunctionDefinitionDataType; -import ghidra.program.model.data.GenericCallingConvention; -import ghidra.program.model.lang.Language; -import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.SourceType; @@ -114,7 +112,12 @@ public class UpgradeDexToGhidra71Script extends GhidraScript { private void processFunction(Function func) { monitor.setMessage("Updating: "+func.getName()); FunctionDefinitionDataType sig = new FunctionDefinitionDataType(func,false); - sig.setGenericCallingConvention(GenericCallingConvention.stdcall); + try { + sig.setCallingConvention(CompilerSpec.CALLING_CONVENTION_stdcall); + } + catch (InvalidInputException e) { + throw new AssertException(e); + } func.setCustomVariableStorage(false); ApplyFunctionSignatureCmd cmd = new ApplyFunctionSignatureCmd(func.getEntryPoint(),sig,SourceType.ANALYSIS); cmd.applyTo(func.getProgram()); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java index 6952549667..c423356045 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java @@ -20,8 +20,11 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException; import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber; import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType; import ghidra.app.util.bin.format.pdb2.pdbreader.type.CallingConvention; -import ghidra.program.model.data.*; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.FunctionDefinitionDataType; +import ghidra.program.model.lang.CompilerSpec; import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; /** * Applier for certain function types. @@ -226,9 +229,9 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier { private void setCallingConvention(DefaultPdbApplicator applicator, CallingConvention callingConvention, boolean hasThisPointer) { - GenericCallingConvention convention; + String convention; if (hasThisPointer) { - convention = GenericCallingConvention.thiscall; + convention = CompilerSpec.CALLING_CONVENTION_thiscall; } else { // Since we are a member function, we will always assume a _thiscall... @@ -236,24 +239,30 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier { switch (callingConvention) { // TODO: figure all of these out. case THISCALL: // "this" passed in register (we have not yet seen this) - convention = GenericCallingConvention.thiscall; // Is this correct if in reg? + convention = CompilerSpec.CALLING_CONVENTION_thiscall; // Is this correct if in reg? break; case NEAR_C: // we have seen this one - convention = GenericCallingConvention.cdecl; + convention = CompilerSpec.CALLING_CONVENTION_cdecl; break; case NEAR_VECTOR: // we have seen this one - convention = GenericCallingConvention.vectorcall; + convention = CompilerSpec.CALLING_CONVENTION_vectorcall; break; default: // applicator.getLog().appendMsg( // "TODO: calling convention not implemented for value " + callingConventionVal + // " in " + funcName); //convention = GenericCallingConvention.cdecl; - convention = GenericCallingConvention.cdecl; + convention = CompilerSpec.CALLING_CONVENTION_cdecl; break; } } - functionDefinition.setGenericCallingConvention(convention); + try { + functionDefinition.setCallingConvention(convention); + } + catch (InvalidInputException e) { + applicator.appendLogMsg("Failed to set calling convention `" + convention + "` for " + + functionDefinition.getName()); + } } } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java index 0f4af41818..7dd3b66408 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java @@ -25,8 +25,10 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.type.*; import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; import mdemangler.*; import mdemangler.object.MDObjectCPP; @@ -498,7 +500,7 @@ public class PdbResearch { //============================================================================================== //============================================================================================== static void studyDataTypeConflicts(DefaultPdbApplicator applicator, TaskMonitor monitor) - throws CancelledException { + throws CancelledException, InvalidInputException { DataTypeConflictHandler handler = DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER; DataTypeManager dtm = applicator.getDataTypeManager(); @@ -510,7 +512,7 @@ public class PdbResearch { FunctionDefinitionDataType fn1 = new FunctionDefinitionDataType(CategoryPath.ROOT, "____fn1____", dtm); fn1.setReturnType(pointer1); - fn1.setGenericCallingConvention(GenericCallingConvention.cdecl); + fn1.setCallingConvention(CompilerSpec.CALLING_CONVENTION_cdecl); fn1.setArguments(new ParameterDefinition[0]); Composite internalStruct1 = createComposite(dtm, "____internal____"); @@ -526,7 +528,7 @@ public class PdbResearch { FunctionDefinitionDataType fn2 = new FunctionDefinitionDataType(CategoryPath.ROOT, "____fn2____", dtm); fn2.setReturnType(pointer2); - fn2.setGenericCallingConvention(GenericCallingConvention.cdecl); + fn2.setCallingConvention(CompilerSpec.CALLING_CONVENTION_cdecl); fn2.setArguments(new ParameterDefinition[0]); Composite internalStruct2 = createComposite(dtm, "____internal____"); diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/ConflictHandlerTest2.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/ConflictHandlerTest2.java index 7789e04139..6dd99d64e6 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/ConflictHandlerTest2.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/ConflictHandlerTest2.java @@ -25,6 +25,7 @@ import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; @@ -66,7 +67,7 @@ public class ConflictHandlerTest2 extends AbstractGhidraHeadedIntegrationTest { } @Test - public void testDataTypeConflicts() { + public void testDataTypeConflicts() throws Exception { DataTypeConflictHandler handler = DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER; @@ -77,7 +78,7 @@ public class ConflictHandlerTest2 extends AbstractGhidraHeadedIntegrationTest { FunctionDefinitionDataType fn1 = new FunctionDefinitionDataType(CategoryPath.ROOT, "fn1", dtm); fn1.setReturnType(pointer1); - fn1.setGenericCallingConvention(GenericCallingConvention.cdecl); + fn1.setCallingConvention(CompilerSpec.CALLING_CONVENTION_cdecl); fn1.setArguments(new ParameterDefinition[0]); Composite internalStruct1 = createComposite(dtm, "inner"); @@ -93,7 +94,7 @@ public class ConflictHandlerTest2 extends AbstractGhidraHeadedIntegrationTest { FunctionDefinitionDataType fn2 = new FunctionDefinitionDataType(CategoryPath.ROOT, "fn2", dtm); fn2.setReturnType(pointer2); - fn2.setGenericCallingConvention(GenericCallingConvention.cdecl); + fn2.setCallingConvention(CompilerSpec.CALLING_CONVENTION_cdecl); fn2.setArguments(new ParameterDefinition[0]); Composite internalStruct2 = createComposite(dtm, "inner"); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java index cd69c3a8f0..3ce13bbc51 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java @@ -85,9 +85,7 @@ public class FunctionSignatureStringable extends Stringable { this.signatureSource = function.getSignatureSource(); this.hasCustomStorage = function.hasCustomVariableStorage(); - GenericCallingConvention guessedCallingConvention = - GenericCallingConvention.guessFromName(function.getCallingConventionName()); - isThisCall = (guessedCallingConvention == GenericCallingConvention.thiscall); + isThisCall = CompilerSpec.CALLING_CONVENTION_thiscall.equals(callingConventionName); // ignore source from function return which is same as signature source returnInfo = getParameterInfo(function.getReturn(), SourceType.DEFAULT); @@ -531,7 +529,7 @@ public class FunctionSignatureStringable extends Stringable { if (hasCustomStorage != toFunction.hasCustomVariableStorage()) { // This should only change to use custom storage if same language. boolean sameLanguage = - FunctionUtility.isSameLanguage(toFunction.getProgram(), program); + FunctionUtility.isSameLanguageAndCompilerSpec(toFunction.getProgram(), program); if (!hasCustomStorage || (hasCustomStorage && sameLanguage)) { useCustomStorage = hasCustomStorage; } @@ -673,7 +671,7 @@ public class FunctionSignatureStringable extends Stringable { } if (callFixupChoice == ReplaceChoices.REPLACE) { // Check that you have the same cspec before trying to apply call fixup. - if (FunctionUtility.isSameLanguage(toFunction.getProgram(), program)) { + if (FunctionUtility.isSameLanguageAndCompilerSpec(toFunction.getProgram(), program)) { toFunction.setCallFixup(fromFunctionCallFixup); } } @@ -692,7 +690,7 @@ public class FunctionSignatureStringable extends Stringable { switch (callingConventionChoice) { case SAME_LANGUAGE: - if (FunctionUtility.isSameLanguage(program, toProgram)) { + if (FunctionUtility.isSameLanguageAndCompilerSpec(program, toProgram)) { return callingConventionName; } break; diff --git a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java index c3d9299e4e..98909d6ee4 100644 --- a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java +++ b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java @@ -1055,7 +1055,7 @@ public class DBHandle { Table table; synchronized (this) { if (tables.containsKey(name)) { - throw new IOException("Table already exists"); + throw new IOException("Table already exists: " + name); } checkTransaction(); table = new Table(this, masterTable.createTableRecord(name, schema, -1)); @@ -1084,7 +1084,7 @@ public class DBHandle { } checkTransaction(); if (tables.containsKey(newName)) { - throw new DuplicateNameException("Table already exists"); + throw new DuplicateNameException("Table already exists: " + newName); } Table table = tables.remove(oldName); if (table == null) { diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/SpecXmlUtils.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/SpecXmlUtils.java index fadf2b4786..58d7b92187 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/SpecXmlUtils.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/xml/SpecXmlUtils.java @@ -64,6 +64,14 @@ public class SpecXmlUtils { return false; } + static public boolean decodeBoolean(String val, boolean defaultValue) { + Boolean returnValue = decodeNullableBoolean(val); + if (returnValue != null) { + return returnValue; + } + return defaultValue; + } + static public String encodeBoolean(boolean val) { return val ? "true" : "false"; } diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg index ade4ca69e3..7ab59e71a6 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg @@ -345,11 +345,6 @@ - - - - - diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java index 6648157c64..ddce23a38b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java @@ -23,7 +23,6 @@ import ghidra.framework.Application; import ghidra.framework.data.DomainObjectAdapterDB; import ghidra.framework.model.*; import ghidra.framework.options.Options; -import ghidra.program.database.data.ProjectDataTypeManager; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.PointerDataType; import ghidra.program.model.listing.DataTypeArchive; @@ -32,7 +31,6 @@ import ghidra.program.util.*; import ghidra.util.InvalidNameException; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -import ghidra.util.task.TaskMonitorAdapter; /** * Database implementation for Data Type Archive. @@ -493,7 +491,7 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB // } try { - dataTypeManager = new ProjectDataTypeManager(dbh, openMode, this, lock, monitor); + dataTypeManager = new ProjectDataTypeManager(this, dbh, openMode, this, lock, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); @@ -506,7 +504,6 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB private void initManagers(int openMode, TaskMonitor monitor) throws IOException, CancelledException { monitor.checkCanceled(); - dataTypeManager.setDataTypeArchive(this); dataTypeManager.archiveReady(openMode, monitor); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java index 80cfeb8140..17d754799e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java @@ -36,7 +36,6 @@ import ghidra.program.database.code.InstructionDB; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.database.external.ExternalManagerDB; import ghidra.program.database.function.FunctionManagerDB; -import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMapDB; import ghidra.program.database.mem.MemoryMapDB; import ghidra.program.database.module.TreeManager; @@ -745,13 +744,13 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM /** * Returns this programs address map. - * NOTE: This method has been dropped from the Program interface to help - * discourage the use of the program's address map since bad assumptions + * NOTE: This method should be dropped from the {@link Program} interface to help + * discourage the its use external to this implementation since bad assumptions * are frequently made about address keys which may not be ordered or sequential * across an entire address space. */ @Override - public AddressMap getAddressMap() { + public AddressMapDB getAddressMap() { return addrMap; } @@ -1678,7 +1677,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM monitor.checkCanceled(); try { - managers[SYMBOL_MGR] = new SymbolManager(dbh, addrMap, openMode, lock, monitor); + managers[SYMBOL_MGR] = new SymbolManager(dbh, addrMap, openMode, this, lock, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); @@ -2069,6 +2068,9 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM contextMgr.initializeDefaultValues(language, compilerSpec); } + // Update datatype manager data organization + getDataTypeManager().languageChanged(monitor); + // Force function manager to reconcile calling conventions managers[FUNCTION_MGR].setProgram(this); managers[FUNCTION_MGR].programReady(UPDATE, getStoredVersion(), monitor); @@ -2443,6 +2445,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM lock.acquire(); try { ((ProgramCompilerSpec) compilerSpec).installExtensions(); + getFunctionManager().invalidateCache(true); + getDataTypeManager().invalidateCache(); } finally { lock.release(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java similarity index 76% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java index 41b4f5b6fd..abace47dc5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProjectDataTypeManager.java @@ -13,17 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.program.database.data; +package ghidra.program.database; import java.io.IOException; -import java.util.Iterator; import java.util.LinkedList; +import javax.help.UnsupportedOperationException; + import db.*; import db.util.ErrorHandler; import ghidra.framework.model.DomainFile; -import ghidra.program.database.DataTypeArchiveDB; +import ghidra.framework.store.LockException; import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; import ghidra.program.util.DataTypeArchiveChangeManager; import ghidra.util.InvalidNameException; import ghidra.util.Lock; @@ -35,13 +38,23 @@ import ghidra.util.task.TaskMonitor; * Class for managing data types in a project archive * NOTE: default data organization is used. */ -public class ProjectDataTypeManager extends DataTypeManagerDB +public class ProjectDataTypeManager extends StandAloneDataTypeManager implements ProjectArchiveBasedDataTypeManager { - private DataTypeArchiveDB dataTypeArchive; + private final DataTypeArchiveDB dataTypeArchive; /** - * Constructor + * Constructor for a data-type manager using a specified DBHandle. + *

+ * NOTE: If archive has an assigned architecture, issues may arise due to a revised or + * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not + * prevent the archive from being opened. Such a warning condition will ne logged and may + * result in missing or stale information for existing datatypes which have architecture related + * data. In some case it may be appropriate to + * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive + * object prior to its use. + * + * @param dataTypeArchive associated archive * @param handle open database handle * @param openMode the program open mode * @param errHandler the database I/O error handler @@ -51,17 +64,12 @@ public class ProjectDataTypeManager extends DataTypeManagerDB * @throws VersionException if the database does not match the expected version. * @throws IOException if a database I/O error occurs. */ - public ProjectDataTypeManager(DBHandle handle, int openMode, ErrorHandler errHandler, Lock lock, + ProjectDataTypeManager(DataTypeArchiveDB dataTypeArchive, DBHandle handle, int openMode, + ErrorHandler errHandler, Lock lock, TaskMonitor monitor) throws CancelledException, VersionException, IOException { - super(handle, null, openMode, errHandler, lock, monitor); - } - - /** - * Set the associated Archive - * @param dtArchive associated archive - */ - public void setDataTypeArchive(DataTypeArchiveDB dtArchive) { - this.dataTypeArchive = dtArchive; + super(handle, openMode, errHandler, lock, monitor); + this.dataTypeArchive = dataTypeArchive; + reportWarning(); } @Override @@ -69,11 +77,6 @@ public class ProjectDataTypeManager extends DataTypeManagerDB return dataTypeArchive.getDomainFile().getName(); } - @Override - public Pointer getPointer(DataType dt) { - return PointerDataType.getPointer(dt, dataTypeArchive.getDefaultPointerSize()); - } - @Override public void setName(String name) throws InvalidNameException { if (name == null || name.length() == 0) { @@ -84,6 +87,23 @@ public class ProjectDataTypeManager extends DataTypeManagerDB categoryRenamed(CategoryPath.ROOT, null); } + @Override + public void clearProgramArchitecture(TaskMonitor monitor) + throws CancelledException, IOException, LockException { + dataTypeArchive.checkExclusiveAccess(); + super.clearProgramArchitecture(monitor); + } + + @Override + public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + LockException, UnsupportedOperationException, IncompatibleLanguageException, + CancelledException { + dataTypeArchive.checkExclusiveAccess(); + super.setProgramArchitecture(language, compilerSpecId, updateOption, monitor); + } + //////////////////// @Override public void dataTypeChanged(DataType dt, boolean isAutoChange) { @@ -169,21 +189,12 @@ public class ProjectDataTypeManager extends DataTypeManagerDB /////////////////// @Override protected void replaceDataTypeIDs(long oldDataTypeID, long newDataTypeID) { -// dataTypeArchive.getCodeManager().replace(oldID, newID, monitor); - // TODO + // do nothing } @Override - protected void deleteDataTypeIDs(LinkedList deletedIds, TaskMonitor monitor) - throws CancelledException { - long[] ids = new long[deletedIds.size()]; - Iterator it = deletedIds.iterator(); - int i = 0; - while (it.hasNext()) { - ids[i++] = it.next().longValue(); - } -// dataTypeArchive.getCodeManager().clearData(ids, monitor); - // TODO + protected void deleteDataTypeIDs(LinkedList deletedIds, TaskMonitor monitor) { + // do nothing } @Override @@ -191,6 +202,7 @@ public class ProjectDataTypeManager extends DataTypeManagerDB return dataTypeArchive.openTransaction(description); } + @SuppressWarnings("sync-override") @Override public int startTransaction(String description) { return dataTypeArchive.startTransaction(description); @@ -201,6 +213,7 @@ public class ProjectDataTypeManager extends DataTypeManagerDB dataTypeArchive.flushEvents(); } + @SuppressWarnings("sync-override") @Override public void endTransaction(int transactionID, boolean commit) { dataTypeArchive.endTransaction(transactionID, commit); @@ -231,7 +244,7 @@ public class ProjectDataTypeManager extends DataTypeManagerDB public void archiveReady(int openMode, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == DBConstants.UPGRADE) { - doSourceArchiveUpdates(null, monitor); + doSourceArchiveUpdates(monitor); migrateOldFlexArrayComponentsIfRequired(monitor); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java index 01372bef38..cab9d93887 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java @@ -721,7 +721,7 @@ public class SpecExtension { PrototypeModel currentModel = function.getCallingConvention(); if (currentModel != null && currentModel.getName().equals(modelName)) { try { - function.setCallingConvention("unknown"); + function.setCallingConvention(Function.UNKNOWN_CALLING_CONVENTION_STRING); } catch (InvalidInputException e) { // shouldn't reach here diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java index 2dfe56cdec..d1c446d444 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java @@ -47,6 +47,7 @@ class DataDB extends CodeUnitDB implements Data { protected DataType baseDataType; protected int level = 0; + protected ProgramDataTypeManager dataMgr; private Boolean hasMutabilitySetting; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java index ba42612d9e..af5ed4b49e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -33,13 +34,25 @@ abstract class ArrayDBAdapter { static final int ARRAY_ELEMENT_LENGTH_COL = ArrayDBAdapterV1.V1_ARRAY_ELEMENT_LENGTH_COL; static final int ARRAY_CAT_COL = ArrayDBAdapterV1.V1_ARRAY_CAT_COL; - static ArrayDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + /** + * Gets an adapter for working with the {@link ArrayDB} database table. + * @param handle handle to the database to be accessed. + * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor + * @return adapter instance + * @throws VersionException if the database handle's version doesn't match the expected version. + * @throws IOException if there is a problem accessing the database. + * @throws CancelledException task cancelled + */ + static ArrayDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { if (openMode == DBConstants.CREATE) { - return new ArrayDBAdapterV1(handle, true); + return new ArrayDBAdapterV1(handle, tablePrefix, true); } try { - return new ArrayDBAdapterV1(handle, false); + return new ArrayDBAdapterV1(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -47,33 +60,36 @@ abstract class ArrayDBAdapter { } ArrayDBAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } } - static ArrayDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { + private static ArrayDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { return new ArrayDBAdapterV0(handle); } - static ArrayDBAdapter upgrade(DBHandle handle, ArrayDBAdapter oldAdapter) - throws VersionException, IOException { + private static ArrayDBAdapter upgrade(DBHandle handle, ArrayDBAdapter oldAdapter, + String tablePrefix, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); ArrayDBAdapter tmpAdapter = null; try { - tmpAdapter = new ArrayDBAdapterV1(tmpHandle, true); + tmpAdapter = new ArrayDBAdapterV1(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec); } oldAdapter.deleteTable(handle); - ArrayDBAdapterV1 newAdapter = new ArrayDBAdapterV1(handle, true); + ArrayDBAdapterV1 newAdapter = new ArrayDBAdapterV1(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java index 8e6c3e10e5..3ef1f94bc5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java @@ -24,13 +24,14 @@ import ghidra.util.exception.VersionException; * */ class ArrayDBAdapterV0 extends ArrayDBAdapter { + + private static final int VERSION = 0; + private static final String ARRAY_TABLE_NAME = "Arrays"; private static final int V0_ARRAY_DT_ID_COL = 0; private static final int V0_ARRAY_DIM_COL = 1; private static final int V0_ARRAY_ELEMENT_LENGTH_COL = 2; // applies to sizable dynamic types only - private Table table; - // DO NOT REMOVE - this documents the schema used in version 0. // public static final Schema SCHEMA = new Schema(0, "Array ID", // new Class[] {LongField.class, IntField.class, @@ -38,9 +39,13 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter { // new String[] {"Data Type ID", "Dimension", // "Length"}); + private Table table; + /** - * Constructor - * + * Gets a version 0 read-only adapter for the {@link ArrayDB} database table. + * @param handle handle to the database containing the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. */ public ArrayDBAdapterV0(DBHandle handle) throws VersionException { @@ -48,9 +53,8 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter { if (table == null) { throw new VersionException("Missing Table: " + ARRAY_TABLE_NAME); } - else if (table.getSchema().getVersion() != 0) { - throw new VersionException("Expected version 0 for table " + ARRAY_TABLE_NAME + - " but got " + table.getSchema().getVersion()); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java index c1ec6c3779..938f736c9c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java @@ -25,40 +25,49 @@ import ghidra.util.exception.VersionException; * To change the template for this generated type comment go to * {@literal Window>Preferences>Java>Code Generation>Code and Comments} * - * + * NOTE: Use of tablePrefix introduced with this adapter version. */ class ArrayDBAdapterV1 extends ArrayDBAdapter { + static final int VERSION = 1; + static final String ARRAY_TABLE_NAME = "Arrays"; static final int V1_ARRAY_DT_ID_COL = 0; static final int V1_ARRAY_DIM_COL = 1; static final int V1_ARRAY_ELEMENT_LENGTH_COL = 2; // applies to sizable dynamic types only static final int V1_ARRAY_CAT_COL = 3; - private Table table; - public static final Schema V1_SCHEMA = new Schema(VERSION, "Array ID", new Field[] { LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE }, new String[] { "Data Type ID", "Dimension", "Length", "Cat ID" }); - /** - * Constructor - * - */ - public ArrayDBAdapterV1(DBHandle handle, boolean create) throws VersionException, IOException { + private Table table; + /** + * Gets a version 1 adapter for the {@link ArrayDB} database table. + * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name + * @param create create table if true else acquire for read-only or update use + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException an IO error occured during table creation + */ + public ArrayDBAdapterV1(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + ARRAY_TABLE_NAME; if (create) { - table = handle.createTable(ARRAY_TABLE_NAME, V1_SCHEMA, new int[] { V1_ARRAY_CAT_COL }); + table = handle.createTable(tableName, V1_SCHEMA, new int[] { V1_ARRAY_CAT_COL }); } else { - table = handle.getTable(ARRAY_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { - throw new VersionException("Missing Table: " + ARRAY_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } - else if (table.getSchema().getVersion() != VERSION) { - throw new VersionException(VersionException.NEWER_VERSION, false); + int version = table.getSchema().getVersion(); + if (version != VERSION) { + throw new VersionException(version < VERSION); } } } @@ -67,11 +76,7 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { public DBRecord createRecord(long dataTypeID, int numberOfElements, int length, long catID) throws IOException { - long tableKey = table.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.ARRAY, tableKey); + long key = DataTypeManagerDB.createKey(DataTypeManagerDB.ARRAY, table.getKey()); DBRecord record = V1_SCHEMA.createRecord(key); record.setLongValue(V1_ARRAY_DT_ID_COL, dataTypeID); @@ -105,7 +110,7 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { @Override void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(ARRAY_TABLE_NAME); + handle.deleteTable(table.getName()); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java index 809a6f4097..b146cf8e80 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java @@ -19,7 +19,6 @@ import java.io.IOException; import db.*; import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; /** * Database adapter for managing built-in data types. @@ -35,14 +34,14 @@ public abstract class BuiltinDBAdapter { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). - * @param monitor the monitor to use for displaying status or for canceling. + * @param tablePrefix prefix to be used with default table name * @return the adapter for accessing the table of built-in data types. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there was a problem accessing the database */ - static BuiltinDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) + static BuiltinDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix) throws VersionException, IOException { - return new BuiltinDBAdapterV0(handle, openMode == DBConstants.CREATE); + return new BuiltinDBAdapterV0(handle, tablePrefix, openMode == DBConstants.CREATE); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java index 9075a5c7f8..c6525617f0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java @@ -24,38 +24,44 @@ import ghidra.util.exception.VersionException; * Version 0 implementation of the adapter for accessing the built-ins table. */ class BuiltinDBAdapterV0 extends BuiltinDBAdapter { + + private static final int VERSION = 0; + static final String BUILT_IN_TABLE_NAME = "Built-in datatypes"; static final int V0_BUILT_IN_NAME_COL = 0; static final int V0_BUILT_IN_CLASSNAME_COL = 1; static final int V0_BUILT_IN_CAT_COL = 2; + static final Schema V0_SCHEMA = new Schema(0, "Data Type ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE }, new String[] { "Name", "Class Name", "Category ID" }); + private Table table; /** * Gets a version 0 adapter for the Built-Ins database table. * @param handle handle to the database containing the table. - * @param create true if this constructor should create the table. + * @param tablePrefix prefix to be used with default table name + * @param create create table if true else acquire for read-only or update use * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if there is trouble accessing the database. */ - public BuiltinDBAdapterV0(DBHandle handle, boolean create) + public BuiltinDBAdapterV0(DBHandle handle, String tablePrefix, boolean create) throws VersionException, IOException { + String tableName = tablePrefix + BUILT_IN_TABLE_NAME; if (create) { - table = handle.createTable(BUILT_IN_TABLE_NAME, V0_SCHEMA, + table = handle.createTable(tableName, V0_SCHEMA, new int[] { V0_BUILT_IN_CAT_COL }); } else { - table = handle.getTable(BUILT_IN_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { - throw new VersionException("Missing Table: " + BUILT_IN_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } - else if (table.getSchema().getVersion() != 0) { - throw new VersionException("Expected version 0 for table " + BUILT_IN_TABLE_NAME + - " but got " + table.getSchema().getVersion()); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapter.java new file mode 100644 index 0000000000..8001512af3 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapter.java @@ -0,0 +1,119 @@ +/* ### + * 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 java.io.IOException; +import java.util.Set; +import java.util.function.Consumer; + +import db.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; + +/** + * Adapter to access the Function Calling Conventions tables. + */ +abstract class CallingConventionDBAdapter { + + static final byte UNKNOWN_CALLING_CONVENTION_ID = (byte) 0; + static final byte DEFAULT_CALLING_CONVENTION_ID = (byte) 1; + static final byte FIRST_CALLING_CONVENTION_ID = (byte) 2; + + static final String CALLING_CONVENTION_TABLE_NAME = "Calling Conventions"; + + static final Schema CALLING_CONVENTION_SCHEMA = + CallingConventionDBAdapterV0.V0_CALLING_CONVENTION_SCHEMA; + // Calling Convention Columns + static final int CALLING_CONVENTION_NAME_COL = + CallingConventionDBAdapterV0.V0_CALLING_CONVENTION_NAME_COL; + + /** + * Gets an adapter for working with the calling convention database table. + * @param handle handle to the database to be accessed. + * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor + * @return adapter instance + * @throws VersionException if the database handle's version doesn't match the expected version. + * @throws IOException if there is a problem accessing the database. + * @throws CancelledException if task is cancelled + */ + static CallingConventionDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { + if (openMode == DBConstants.CREATE) { + return new CallingConventionDBAdapterV0(handle, tablePrefix, true); + } + try { + return new CallingConventionDBAdapterV0(handle, tablePrefix, false); + } + catch (VersionException e) { + if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { + throw e; + } + CallingConventionDBAdapter adapter = findReadOnlyAdapter(handle); + if (openMode == DBConstants.UPGRADE) { + adapter = upgrade(handle, adapter, tablePrefix, monitor); + } + return adapter; + } + } + + private static CallingConventionDBAdapter findReadOnlyAdapter(DBHandle handle) { + return new CallingConventionDBAdapterNoTable(); + } + + private static CallingConventionDBAdapter upgrade(DBHandle handle, + CallingConventionDBAdapter oldAdapter, String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException { + return new CallingConventionDBAdapterV0(handle, tablePrefix, true); + } + + /** + * Get (and assign if needed thus requiring open transaction) the ID associated with the + * specified calling convention name. If name is a new convention and the number of stored + * convention names exceeds 127 the returned ID will correspond to the unknown calling + * convention. + * @param name calling convention name or null if unknown + * @param conventionAdded callback when new calling convention is added + * @return calling convention ID + * @throws IOException if an IO error occurs + */ + abstract byte getCallingConventionId(String name, Consumer conventionAdded) + throws IOException; + + /** + * Get calling convention name which corresponds to the specified id. + * @param id calling convention storage ID + * @return calling convention name or null if unknown call convention + * @throws IOException if IO error occurs + */ + abstract String getCallingConventionName(byte id) throws IOException; + + /** + * Clear calling convention cached lookup maps + */ + abstract void invalidateCache(); + + /** + * Get all stored calling convention names. The "default" and "unknown" + * names are excluded from this set. + * @return set of all stored calling convention names + * @throws IOException if an IO error occurs + */ + abstract Set getCallingConventionNames() throws IOException; + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapterNoTable.java new file mode 100644 index 0000000000..d09de20140 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapterNoTable.java @@ -0,0 +1,56 @@ +/* ### + * 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 java.io.IOException; +import java.util.Set; +import java.util.function.Consumer; + +import javax.help.UnsupportedOperationException; + +/** + * Adapter when no Calling Convention table exists. + */ +class CallingConventionDBAdapterNoTable extends CallingConventionDBAdapter { + + /** + * Gets a no-table adapter for the calling convention database table. + */ + CallingConventionDBAdapterNoTable() { + // no table - do nothing + } + + @Override + byte getCallingConventionId(String name, Consumer conventionAdded) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + String getCallingConventionName(byte id) throws IOException { + return null; + } + + @Override + void invalidateCache() { + // do nothing + } + + @Override + Set getCallingConventionNames() throws IOException { + return Set.of(); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapterV0.java new file mode 100644 index 0000000000..8815377062 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CallingConventionDBAdapterV0.java @@ -0,0 +1,187 @@ +/* ### + * 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 java.io.IOException; +import java.util.*; +import java.util.function.Consumer; + +import com.google.common.collect.Range; +import com.google.common.collect.TreeRangeSet; + +import db.*; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.listing.Function; +import ghidra.util.Msg; +import ghidra.util.exception.VersionException; + +/** + * Version 0 implementation for the calling conventions tables adapter. + * + */ +class CallingConventionDBAdapterV0 extends CallingConventionDBAdapter { + + private static final int VERSION = 0; + + // Calling Convention Columns + // Key field is the Calling convention ID, which is a Byte field. + static final int V0_CALLING_CONVENTION_NAME_COL = 0; + + static final Schema V0_CALLING_CONVENTION_SCHEMA = new Schema(0, ByteField.INSTANCE, "ID", + new Field[] { StringField.INSTANCE }, new String[] { "Name" }); + + private Table callingConventionTable; + + private Map callingConventionNameToIDMap; + private Map callingConventionIDToNameMap; + + // There is currently no method for removing an allocated calling convention name/ID, + // therefor we can assume key consumption will be sequential until the ability + // to delete is added. Use of freeKeySet can be eliminated if delete ability never added. + private TreeRangeSet freeKeySet; // closed-ranges only + + /** + * Gets a version 0 adapter for the calling convention database table. + * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if an IO error occurs + */ + CallingConventionDBAdapterV0(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + CALLING_CONVENTION_TABLE_NAME; + if (create) { + // No additional indexed fields. + callingConventionTable = handle.createTable(tableName, + V0_CALLING_CONVENTION_SCHEMA, new int[] {}); + } + else { + callingConventionTable = handle.getTable(tableName); + if (callingConventionTable == null) { + throw new VersionException(true); + } + if (callingConventionTable.getSchema().getVersion() != VERSION) { + throw new VersionException(false); + } + } + } + + /** + * Remove next available free key value from freeKeySet. + * @return the next available key. A negative value indicates that all allowed IDs have + * been used. + */ + private byte removeFirstAvailableKey() { + Iterator> it = freeKeySet.asRanges().iterator(); + if (!it.hasNext()) { + return -1; + } + Range r = it.next(); + it.remove(); + byte nextId = r.lowerEndpoint(); + byte lastId = r.upperEndpoint(); + if (nextId != lastId) { + freeKeySet.add(Range.closed((byte) (nextId + 1), lastId)); + } + return nextId; + } + + @Override + void invalidateCache() { + callingConventionNameToIDMap = null; + callingConventionIDToNameMap = null; + freeKeySet = null; + } + + private void populateCache() throws IOException { + if (callingConventionNameToIDMap != null) { + return; + } + callingConventionNameToIDMap = new HashMap<>(); + callingConventionIDToNameMap = new HashMap<>(); + + freeKeySet = TreeRangeSet.create(); + int nextKey = FIRST_CALLING_CONVENTION_ID; + RecordIterator iterator = callingConventionTable.iterator(); + while (iterator.hasNext()) { + DBRecord rec = iterator.next(); + + byte id = (byte) rec.getKey(); + String name = rec.getString(V0_CALLING_CONVENTION_NAME_COL); + callingConventionIDToNameMap.put(id, name); + callingConventionNameToIDMap.put(name, id); + + if (nextKey != id) { + freeKeySet.add(Range.closed((byte) nextKey, (byte) (id - 1))); + } + nextKey = id + 1; + } + if (nextKey <= Byte.MAX_VALUE) { + freeKeySet.add(Range.closed((byte) nextKey, Byte.MAX_VALUE)); + } + } + + @Override + byte getCallingConventionId(String name, Consumer conventionAdded) throws IOException { + if (name == null || name.equals(CompilerSpec.CALLING_CONVENTION_unknown)) { + return UNKNOWN_CALLING_CONVENTION_ID; + } + else if (name.equals(CompilerSpec.CALLING_CONVENTION_default)) { + return DEFAULT_CALLING_CONVENTION_ID; + } + populateCache(); + Byte id = callingConventionNameToIDMap.get(name); + if (id != null) { + return id; + } + + byte newId = removeFirstAvailableKey(); + if (newId < 0) { + Msg.error(this, "Unable to assign calling convention `" + name + + "` - allocation capacity exceeded"); + return UNKNOWN_CALLING_CONVENTION_ID; + } + + DBRecord record = V0_CALLING_CONVENTION_SCHEMA.createRecord(new ByteField(newId)); + record.setString(V0_CALLING_CONVENTION_NAME_COL, name); + callingConventionTable.putRecord(record); + + callingConventionIDToNameMap.put(newId, name); + callingConventionNameToIDMap.put(name, newId); + conventionAdded.accept(name); + return newId; + } + + @Override + String getCallingConventionName(byte id) throws IOException { + if (id == DEFAULT_CALLING_CONVENTION_ID) { + return Function.DEFAULT_CALLING_CONVENTION_STRING; + } + else if (id == UNKNOWN_CALLING_CONVENTION_ID) { + return null; + } + populateCache(); + return callingConventionIDToNameMap.get(id); + } + + @Override + Set getCallingConventionNames() throws IOException { + populateCache(); + return callingConventionNameToIDMap.keySet(); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java index 1cf7867ef7..3f0783ac78 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java @@ -18,16 +18,25 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.program.model.data.Category; import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; abstract class CategoryDBAdapter { static final int CATEGORY_NAME_COL = CategoryDBAdapterV0.V0_CATEGORY_NAME_COL; static final int CATEGORY_PARENT_COL = CategoryDBAdapterV0.V0_CATEGORY_PARENT_COL; - static CategoryDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) + /** + * Gets an adapter for working with the {@link Category} database table. + * @param handle handle to the database to be accessed. + * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name + * @return adapter instance + * @throws VersionException if the database handle's version doesn't match the expected version. + * @throws IOException if there is a problem accessing the database. + */ + static CategoryDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix) throws VersionException, IOException { - return new CategoryDBAdapterV0(handle, openMode); + return new CategoryDBAdapterV0(handle, tablePrefix, openMode == DBConstants.CREATE); } /** @@ -81,7 +90,16 @@ abstract class CategoryDBAdapter { */ abstract DBRecord getRootRecord() throws IOException; + /** + * Update record in database + * @param record category record + * @throws IOException if IO error occurs + */ abstract void putRecord(DBRecord record) throws IOException; + /** + * Get the total number of category records + * @return category record count + */ abstract int getRecordCount(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java index 62f260be8c..6c45bab1bb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java @@ -21,9 +21,13 @@ import db.*; import ghidra.util.exception.VersionException; class CategoryDBAdapterV0 extends CategoryDBAdapter { + + private static final int VERSION = 0; + static final String CATEGORY_TABLE_NAME = "Categories"; static final int V0_CATEGORY_NAME_COL = 0; static final int V0_CATEGORY_PARENT_COL = 1; + static final Schema V0_SCHEMA = new Schema(0, "Category ID", new Field[] { StringField.INSTANCE, LongField.INSTANCE }, new String[] { "Name", "Parent ID" }); @@ -31,34 +35,38 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { private Table table; /** - * Constructor - * + * Gets a version 0 adapter for the Category database table. + * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if an IO error occurs */ - public CategoryDBAdapterV0(DBHandle handle, int openMode) throws VersionException, IOException { - - if (openMode == DBConstants.CREATE) { - table = handle.createTable(CATEGORY_TABLE_NAME, V0_SCHEMA, - new int[] { V0_CATEGORY_PARENT_COL }); + CategoryDBAdapterV0(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + CATEGORY_TABLE_NAME; + if (create) { + table = handle.createTable(tableName, V0_SCHEMA, new int[] { V0_CATEGORY_PARENT_COL }); } else { - table = handle.getTable(CATEGORY_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { - throw new VersionException("Missing Table: " + CATEGORY_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } - else if (table.getSchema().getVersion() != 0) { - throw new VersionException("Expected version 0 for table " + CATEGORY_TABLE_NAME + - " but got " + table.getSchema().getVersion()); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } } @Override - public DBRecord getRecord(long categoryID) throws IOException { + DBRecord getRecord(long categoryID) throws IOException { return table.getRecord(categoryID); } @Override - public Field[] getRecordIdsWithParent(long categoryID) throws IOException { + Field[] getRecordIdsWithParent(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V0_CATEGORY_PARENT_COL); } @@ -76,7 +84,7 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { } @Override - public DBRecord createCategory(String name, long parentID) throws IOException { + DBRecord createCategory(String name, long parentID) throws IOException { long key = table.getKey(); if (key == 0) { key = 1; @@ -90,12 +98,12 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { } @Override - public boolean removeCategory(long categoryID) throws IOException { + boolean removeCategory(long categoryID) throws IOException { return table.deleteRecord(categoryID); } @Override - public DBRecord getRootRecord() throws IOException { + DBRecord getRootRecord() throws IOException { Field[] keys = table.findRecords(new LongField(-1), V0_CATEGORY_PARENT_COL); if (keys.length != 1) { throw new IOException("Found " + keys.length + " entries for root category"); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java index 021e0c8114..f70793ad9d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java @@ -19,7 +19,6 @@ import java.io.IOException; import db.*; import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; /** * Adapter to access the Component database table. @@ -43,14 +42,14 @@ abstract class ComponentDBAdapter { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). - * @param monitor the monitor to use for displaying status or for canceling. + * @param tablePrefix prefix to be used with default table name * @return the adapter for accessing the table of component data types. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is a problem accessing the database. */ - static ComponentDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) + static ComponentDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix) throws VersionException, IOException { - return new ComponentDBAdapterV0(handle, openMode == DBConstants.CREATE); + return new ComponentDBAdapterV0(handle, tablePrefix, openMode == DBConstants.CREATE); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java index 69f8f3a1aa..614ccfa3aa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java @@ -40,48 +40,41 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter { StringField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE }, new String[] { "Parent", "Offset", "Data Type ID", "Field Name", "Comment", "Component Size", "Ordinal" }); + private Table componentTable; /** * Gets a version 0 adapter for the Component database table. * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. + * @throws IOException if an IO error occurs */ - public ComponentDBAdapterV0(DBHandle handle, boolean create) + ComponentDBAdapterV0(DBHandle handle, String tablePrefix, boolean create) throws VersionException, IOException { - + String tableName = tablePrefix + COMPONENT_TABLE_NAME; if (create) { - componentTable = handle.createTable(COMPONENT_TABLE_NAME, V0_COMPONENT_SCHEMA, + componentTable = handle.createTable(tableName, V0_COMPONENT_SCHEMA, new int[] { V0_COMPONENT_PARENT_ID_COL }); } else { - componentTable = handle.getTable(COMPONENT_TABLE_NAME); + componentTable = handle.getTable(tableName); if (componentTable == null) { - throw new VersionException("Missing Table: " + COMPONENT_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } - int version = componentTable.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + COMPONENT_TABLE_NAME + - " but got " + componentTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + if (componentTable.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } } @Override - public DBRecord createRecord(long dataTypeID, long parentID, int length, int ordinal, int offset, + DBRecord createRecord(long dataTypeID, long parentID, int length, int ordinal, int offset, String name, String comment) throws IOException { - - long tableKey = componentTable.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.COMPONENT, tableKey); + long key = + DataTypeManagerDB.createKey(DataTypeManagerDB.COMPONENT, componentTable.getKey()); DBRecord record = ComponentDBAdapter.COMPONENT_SCHEMA.createRecord(key); record.setLongValue(ComponentDBAdapter.COMPONENT_PARENT_ID_COL, parentID); record.setLongValue(ComponentDBAdapter.COMPONENT_OFFSET_COL, offset); @@ -95,22 +88,22 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter { } @Override - public DBRecord getRecord(long componentID) throws IOException { + DBRecord getRecord(long componentID) throws IOException { return componentTable.getRecord(componentID); } @Override - public void updateRecord(DBRecord record) throws IOException { + void updateRecord(DBRecord record) throws IOException { componentTable.putRecord(record); } @Override - public boolean removeRecord(long componentID) throws IOException { + boolean removeRecord(long componentID) throws IOException { return componentTable.deleteRecord(componentID); } @Override - public Field[] getComponentIdsInComposite(long compositeID) throws IOException { + Field[] getComponentIdsInComposite(long compositeID) throws IOException { return componentTable.findRecords(new LongField(compositeID), ComponentDBAdapter.COMPONENT_PARENT_ID_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java index 291dc5120e..c27e3fe2ee 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java @@ -60,8 +60,8 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal { protected abstract void initialize(); /** - * Get the preferred length for a new component. For Unions and internally - * aligned structures the preferred component length for a fixed-length dataType + * Get the preferred length for a new component. For Unions and packed + * structures the preferred component length for a fixed-length dataType * will be the length of that dataType. Otherwise the length returned will be no * larger than the specified length. * diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java index 2541cdbc82..5ac775aebd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.program.database.util.DBRecordAdapter; import ghidra.program.model.data.CompositeInternal; import ghidra.util.UniversalID; import ghidra.util.exception.CancelledException; @@ -27,7 +28,7 @@ import ghidra.util.task.TaskMonitor; /** * Adapter to access the Composite database table. */ -abstract class CompositeDBAdapter { +abstract class CompositeDBAdapter implements DBRecordAdapter { static final String COMPOSITE_TABLE_NAME = "Composite Data Types"; static final Schema COMPOSITE_SCHEMA = CompositeDBAdapterV5V6.V5V6_COMPOSITE_SCHEMA; @@ -73,24 +74,25 @@ abstract class CompositeDBAdapter { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of composite data types. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. * @throws CancelledException task cancelled */ - static CompositeDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException, CancelledException { + static CompositeDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { try { - return new CompositeDBAdapterV5V6(handle, openMode); + return new CompositeDBAdapterV5V6(handle, openMode, tablePrefix); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { throw e; } - CompositeDBAdapter adapter = findReadOnlyAdapter(handle); + CompositeDBAdapter adapter = findReadOnlyAdapter(handle, tablePrefix); if (openMode == DBConstants.UPGRADE) { - return upgrade(handle, adapter, monitor); + return upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } @@ -99,14 +101,15 @@ abstract class CompositeDBAdapter { /** * Tries to get a read only adapter for the database whose handle is passed to this method. * @param handle handle to prior version of the database. + * @param tablePrefix prefix to be used with default table name * @return the read only Composite data type table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. * @throws IOException if IO error occurs */ - static CompositeDBAdapter findReadOnlyAdapter(DBHandle handle) + private static CompositeDBAdapter findReadOnlyAdapter(DBHandle handle, String tablePrefix) throws VersionException, IOException { try { - return new CompositeDBAdapterV5V6(handle, DBConstants.READ_ONLY); + return new CompositeDBAdapterV5V6(handle, DBConstants.READ_ONLY, tablePrefix); } catch (VersionException e) { // ignore @@ -130,6 +133,7 @@ abstract class CompositeDBAdapter { * Upgrades the Composite data type table from the oldAdapter's version to the current version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. + * @param tablePrefix prefix to be used with default table name * @param monitor task monitor * @return the adapter for the new upgraded version of the table. * @throws VersionException if the the table's version does not match the expected version @@ -137,14 +141,15 @@ abstract class CompositeDBAdapter { * @throws IOException if the database can't be read or written. * @throws CancelledException user cancelled upgrade */ - static CompositeDBAdapter upgrade(DBHandle handle, CompositeDBAdapter oldAdapter, - TaskMonitor monitor) throws VersionException, IOException, CancelledException { + private static CompositeDBAdapter upgrade(DBHandle handle, CompositeDBAdapter oldAdapter, + String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); CompositeDBAdapter tmpAdapter = null; try { - tmpAdapter = new CompositeDBAdapterV5V6(tmpHandle, DBConstants.CREATE); + tmpAdapter = new CompositeDBAdapterV5V6(tmpHandle, DBConstants.CREATE, tablePrefix); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { monitor.checkCanceled(); @@ -152,7 +157,8 @@ abstract class CompositeDBAdapter { tmpAdapter.updateRecord(rec, false); } oldAdapter.deleteTable(handle); - CompositeDBAdapter newAdapter = new CompositeDBAdapterV5V6(handle, DBConstants.CREATE); + CompositeDBAdapter newAdapter = + new CompositeDBAdapterV5V6(handle, DBConstants.CREATE, tablePrefix); if (oldAdapter.getVersion() < FLEX_ARRAY_ELIMINATION_SCHEMA_VERSION) { newAdapter.flexArrayMigrationRequired = true; } @@ -211,7 +217,7 @@ abstract class CompositeDBAdapter { * @return the composite data type record iterator. * @throws IOException if the database can't be accessed. */ - abstract RecordIterator getRecords() throws IOException; + public abstract RecordIterator getRecords() throws IOException; /** * Updates the composite data type table with the provided record. @@ -269,6 +275,6 @@ abstract class CompositeDBAdapter { * Get the number of composite records * @return total number of composite records */ - abstract int getRecordCount(); + public abstract int getRecordCount(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java index 3ed7feb480..646f3eb7c9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java @@ -35,11 +35,12 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato static final int V0_COMPOSITE_LENGTH_COL = 4; static final int V0_COMPOSITE_NUM_COMPONENTS_COL = 5; - static final Schema V0_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", - new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, - LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE }, - new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", - "Number Of Components" }); +// DO NOT REMOVE - this documents the schema used in version 0. +// static final Schema V0_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", +// new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, +// LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE }, +// new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", +// "Number Of Components" }); private Table compositeTable; @@ -49,20 +50,14 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public CompositeDBAdapterV0(DBHandle handle) throws VersionException { + CompositeDBAdapterV0(DBHandle handle) throws VersionException { compositeTable = handle.getTable(COMPOSITE_TABLE_NAME); if (compositeTable == null) { throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME); } - int version = compositeTable.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + - " but got " + compositeTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + if (compositeTable.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } @@ -71,12 +66,12 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } @Override - int getRecordCount() { + public int getRecordCount() { return compositeTable.getRecordCount(); } @Override - public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, + DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime, int packValue, int minAlignment) throws IOException { throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + @@ -84,7 +79,7 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } @Override - public DBRecord getRecord(long dataTypeID) throws IOException { + DBRecord getRecord(long dataTypeID) throws IOException { return translateRecord(compositeTable.getRecord(dataTypeID)); } @@ -94,18 +89,18 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { throw new UnsupportedOperationException(); } @Override - public boolean removeRecord(long compositeID) throws IOException { + boolean removeRecord(long compositeID) throws IOException { throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + " of " + COMPOSITE_TABLE_NAME + " table."); } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java index 71a367b6f2..4a01f3628e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java @@ -38,6 +38,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato static final int V1_COMPOSITE_SOURCE_SYNC_TIME_COL = 8; static final int V1_COMPOSITE_LAST_CHANGE_TIME_COL = 9; +// DO NOT REMOVE - this documents the schema used in version 1. // static final Schema V1_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", // new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, // LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, @@ -54,7 +55,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public CompositeDBAdapterV1(DBHandle handle) throws VersionException { + CompositeDBAdapterV1(DBHandle handle) throws VersionException { compositeTable = handle.getTable(COMPOSITE_TABLE_NAME); if (compositeTable == null) { @@ -62,12 +63,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } int version = compositeTable.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + - " but got " + compositeTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } @@ -76,12 +72,12 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } @Override - int getRecordCount() { + public int getRecordCount() { return compositeTable.getRecordCount(); } @Override - public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, + DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime, int packValue, int minAlignment) throws IOException { throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + @@ -89,7 +85,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } @Override - public DBRecord getRecord(long dataTypeID) throws IOException { + DBRecord getRecord(long dataTypeID) throws IOException { return translateRecord(compositeTable.getRecord(dataTypeID)); } @@ -99,12 +95,12 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { throw new UnsupportedOperationException(); } @Override - public boolean removeRecord(long compositeID) throws IOException { + boolean removeRecord(long compositeID) throws IOException { throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + " of " + COMPOSITE_TABLE_NAME + " table."); } @@ -115,7 +111,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } @@ -126,9 +122,6 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato V1_COMPOSITE_SOURCE_ARCHIVE_ID_COL); } - /* (non-Javadoc) - * @see db.RecordTranslator#translateRecord(db.Record) - */ @Override public DBRecord translateRecord(DBRecord oldRec) { if (oldRec == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java index afc02c2a11..881dbb2bc9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java @@ -51,14 +51,15 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla static final int V2V4_COMPOSITE_PACK_COL = 10; // renamed from Internal Alignment static final int V2V4_COMPOSITE_MIN_ALIGN_COL = 11; // renamed from External Alignment - static final Schema V2V4_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", - new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, - LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, - LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, - IntField.INSTANCE }, - new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", - "Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time", - "Last Change Time", "Pack", "MinAlign" }); +// DO NOT REMOVE - this documents the schema used in versions 2 thru 4. +// static final Schema V2V4_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", +// new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, +// LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, +// LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, +// IntField.INSTANCE }, +// new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", +// "Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time", +// "Last Change Time", "Pack", "MinAlign" }); private Table compositeTable; @@ -68,7 +69,7 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public CompositeDBAdapterV2V4(DBHandle handle) throws VersionException { + CompositeDBAdapterV2V4(DBHandle handle) throws VersionException { compositeTable = handle.getTable(COMPOSITE_TABLE_NAME); if (compositeTable == null) { throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME); @@ -90,12 +91,12 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla } @Override - int getRecordCount() { + public int getRecordCount() { return compositeTable.getRecordCount(); } @Override - public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, + DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime, int packValue, int minAlignment) throws IOException { throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + @@ -103,7 +104,7 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla } @Override - public DBRecord getRecord(long dataTypeID) throws IOException { + DBRecord getRecord(long dataTypeID) throws IOException { return translateRecord(compositeTable.getRecord(dataTypeID)); } @@ -113,12 +114,12 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { throw new UnsupportedOperationException(); } @Override - public boolean removeRecord(long compositeID) throws IOException { + boolean removeRecord(long compositeID) throws IOException { throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + " of " + COMPOSITE_TABLE_NAME + " table."); } @@ -129,7 +130,7 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } @@ -140,9 +141,6 @@ class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTransla V2V4_COMPOSITE_SOURCE_ARCHIVE_ID_COL); } - /* (non-Javadoc) - * @see db.RecordTranslator#translateRecord(db.Record) - */ @Override public DBRecord translateRecord(DBRecord oldRec) { if (oldRec == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java index d2b3b3e914..cb22520b8d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5V6.java @@ -32,6 +32,8 @@ import ghidra.util.exception.VersionException; * Version 6 did not change the schema but corresponds to the elimination * of Structure flex-arrays which are supported in read-only mode under * the older version 5 adapter version. + * + * NOTE: Use of tablePrefix introduced with adapter V6. */ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { @@ -66,33 +68,30 @@ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { /** * Gets an adapter for the Composite database table. * @param handle handle to the database containing the table. - * @param openMode + * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if IO error occurs */ - public CompositeDBAdapterV5V6(DBHandle handle, int openMode) + CompositeDBAdapterV5V6(DBHandle handle, int openMode, String tablePrefix) throws VersionException, IOException { + String tableName = tablePrefix + COMPOSITE_TABLE_NAME; if (openMode == DBConstants.CREATE) { - compositeTable = handle.createTable(COMPOSITE_TABLE_NAME, V5V6_COMPOSITE_SCHEMA, + compositeTable = handle.createTable(tableName, V5V6_COMPOSITE_SCHEMA, new int[] { V5V6_COMPOSITE_CAT_COL, V5V6_COMPOSITE_UNIVERSAL_DT_ID_COL }); } else { - compositeTable = handle.getTable(COMPOSITE_TABLE_NAME); + compositeTable = handle.getTable(tableName); if (compositeTable == null) { - throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } int version = compositeTable.getSchema().getVersion(); - if (version == V5_VERSION && openMode == DBConstants.READ_ONLY) { - return; // StructureDB handles read-only flex-array migration - } if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + - " but got " + version; - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); + if (version == V5_VERSION && openMode == DBConstants.READ_ONLY) { + return; // StructureDB handles read-only flex-array migration } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } } @@ -102,12 +101,12 @@ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { } @Override - int getRecordCount() { + public int getRecordCount() { return compositeTable.getRecordCount(); } @Override - public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, + DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID, int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime, int packValue, int minAlignment) throws IOException { if (compositeTable.getSchema().getVersion() == V5_VERSION) { @@ -141,7 +140,7 @@ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { } @Override - public DBRecord getRecord(long dataTypeID) throws IOException { + DBRecord getRecord(long dataTypeID) throws IOException { return compositeTable.getRecord(dataTypeID); } @@ -151,7 +150,7 @@ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { if (compositeTable.getSchema().getVersion() == V5_VERSION) { throw new UnsupportedOperationException(); } @@ -163,7 +162,7 @@ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { } @Override - public boolean removeRecord(long compositeID) throws IOException { + boolean removeRecord(long compositeID) throws IOException { if (compositeTable.getSchema().getVersion() == V5_VERSION) { throw new UnsupportedOperationException(); } @@ -172,11 +171,11 @@ class CompositeDBAdapterV5V6 extends CompositeDBAdapter { @Override void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(COMPOSITE_TABLE_NAME); + handle.deleteTable(compositeTable.getName()); } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java index b7800805aa..c214cc2ae8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeArchiveTransformer.java @@ -37,6 +37,7 @@ import ghidra.framework.ApplicationConfiguration; import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; import ghidra.program.model.data.Enum; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; import ghidra.util.*; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.exception.*; @@ -63,8 +64,27 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { FileDataTypeManager newFileArchive = null; try { monitor.initialize(100); + oldFileArchive = FileDataTypeManager.openFileArchive(oldFile, false); + ArchiveWarning warning = oldFileArchive.getWarning(); + if (warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED) { + throw new IOException("Archive requires language upgrade: " + oldFile); + } + if (warning != ArchiveWarning.NONE) { + throw new IOException("Archive language error occured: " + oldFile, + oldFileArchive.getWarningDetail()); + } + newFileArchive = FileDataTypeManager.openFileArchive(newFile, true); + warning = newFileArchive.getWarning(); + if (warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED) { + throw new IOException("Archive requires language upgrade: " + newFile); + } + if (warning != ArchiveWarning.NONE) { + throw new IOException("Archive language error occured: " + newFile, + newFileArchive.getWarningDetail()); + } + UniversalID oldUniversalID = oldFileArchive.getUniversalID(); UniversalID newUniversalID = newFileArchive.getUniversalID(); Msg.info(DataTypeArchiveTransformer.class, "Old file ID = " + oldUniversalID); @@ -739,12 +759,10 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable { FileDataTypeManager destinationFileArchive = FileDataTypeManager.openFileArchive(destinationFile, false); - if (destinationFileArchive != null) { - UniversalID destinationUniversalID = destinationFileArchive.getUniversalID(); - destinationFileArchive.close(); - Msg.info(DataTypeArchiveTransformer.class, - "Resulting file ID = " + destinationUniversalID.getValue()); - } + UniversalID destinationUniversalID = destinationFileArchive.getUniversalID(); + destinationFileArchive.close(); + Msg.info(DataTypeArchiveTransformer.class, + "Resulting file ID = " + destinationUniversalID.getValue()); } static File myOldFile = null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java index 2235dbfa24..412334180f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java @@ -305,10 +305,10 @@ class DataTypeComponentDB implements InternalDataTypeComponent { return false; } DataType myParent = getParent(); - boolean aligned = + boolean isPacked = (myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false; - // Components don't need to have matching offset when they are aligned - if ((!aligned && (offset != dtc.getOffset())) || + // Components don't need to have matching offset when structure has packing enabled + if ((!isPacked && (offset != dtc.getOffset())) || !SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) || !SystemUtilities.isEqual(getComment(), dtc.getComment())) { return false; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java index 930c028bcf..7996a6a226 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java @@ -518,9 +518,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType { @Override public SourceArchive getSourceArchive() { - if (dataMgr == null) { - return null; - } return dataMgr.getSourceArchive(getSourceArchiveID()); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java index 63a8026254..9cf7192734 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeIDConverter.java @@ -23,6 +23,7 @@ import ghidra.GhidraLaunchable; import ghidra.framework.Application; import ghidra.framework.ApplicationConfiguration; import ghidra.program.model.data.*; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; import ghidra.util.NumericUtilities; import ghidra.util.UniversalID; import ghidra.util.datastruct.LongLongHashtable; @@ -90,6 +91,10 @@ public class DataTypeIDConverter implements GhidraLaunchable { FileDataTypeManager oldFileArchive = null; try { oldFileArchive = FileDataTypeManager.openFileArchive(inFile, false); + if (oldFileArchive.getWarning() != ArchiveWarning.NONE) { + System.out.println("Archive ID Conversion aborted"); + return; + } UniversalID oldFileUID = oldFileArchive.getUniversalID(); long newID = idMap.get(oldFileUID.getValue()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index e7fa3194f8..45aeb46c00 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -19,24 +19,30 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.help.UnsupportedOperationException; + import db.*; import db.util.ErrorHandler; import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; import ghidra.docking.settings.*; +import ghidra.framework.Application; import ghidra.framework.store.db.PackedDBHandle; import ghidra.framework.store.db.PackedDatabase; import ghidra.graph.*; import ghidra.graph.algo.GraphNavigator; import ghidra.program.database.*; import ghidra.program.database.map.AddressMap; +import ghidra.program.database.symbol.VariableStorageManager; +import ghidra.program.database.util.DBRecordAdapter; import ghidra.program.model.data.*; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.program.model.data.Enum; -import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.*; import ghidra.util.*; import ghidra.util.classfinder.ClassTranslator; import ghidra.util.datastruct.FixedSizeHashMap; @@ -89,9 +95,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager { // Data map keys private static final String DTM_DB_VERSION_KEY = "DB Version"; + private static final String DTM_GHIDRA_VERSION_KEY = "GHIDRA Version"; private static final String SETTINGS_TABLE_NAME = "Default Settings"; + public static final byte UNKNOWN_CALLING_CONVENTION_ID = + CallingConventionDBAdapter.UNKNOWN_CALLING_CONVENTION_ID; + public static final byte DEFAULT_CALLING_CONVENTION_ID = + CallingConventionDBAdapter.DEFAULT_CALLING_CONVENTION_ID; + private BuiltinDBAdapter builtinAdapter; private ComponentDBAdapter componentAdapter; private CompositeDBAdapter compositeAdapter; @@ -107,7 +119,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private ParentChildAdapter parentChildAdapter; protected SourceArchiveAdapter sourceArchiveAdapter; + private CallingConventionDBAdapter callingConventionAdapter; + private TreeSet knownCallingConventions; + private TreeSet definedCallingConventions; + protected DBHandle dbHandle; + private int mode; // open mode (see DBConstants) + protected final String tablePrefix; protected final ErrorHandler errHandler; private DataTypeConflictHandler currentHandler; @@ -143,8 +161,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { protected AddressMap addrMap; - protected DataOrganization dataOrganization; - + private DataOrganization dataOrganization; + private ProgramArchitecture programArchitecture; + private VariableStorageManager variableStorageMgr; + protected final Lock lock; private static class ResolvePair implements Comparable { @@ -186,13 +206,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager { /** * Construct a new temporary data-type manager. Note that this manager does not - * support the save or saveAs operation. + * support the save or saveAs operation. No Language is associated with instance. + * * @param dataOrganization applicable data organization */ protected DataTypeManagerDB(DataOrganization dataOrganization) { this.lock = new Lock("DataTypeManagerDB"); this.errHandler = new DbErrorHandler(); this.dataOrganization = dataOrganization; + this.tablePrefix = ""; try { dbHandle = new DBHandle(); @@ -224,10 +246,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @throws IOException a low-level IO error. This exception may also be thrown * when a version error occurs (cause is VersionException). */ - protected DataTypeManagerDB(ResourceFile packedDBfile, int openMode) throws IOException { + protected DataTypeManagerDB(ResourceFile packedDBfile, int openMode) + throws IOException { this.errHandler = new DbErrorHandler(); - lock = new Lock("DataTypeManagerDB"); + this.lock = new Lock("DataTypeManagerDB"); + this.tablePrefix = ""; File file = packedDBfile.getFile(false); if (file == null && openMode != DBConstants.READ_ONLY) { @@ -267,14 +291,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { boolean initSuccess = false; try { - // TODO: In the future it may be neccessary to store additional properties - // within the archive to failitate use of a language specified data organization. - // It will likely be neccessary to have a different CREATE constructor which accepts - // a DataOrganization - - dataOrganization = DataOrganizationImpl.getDefaultOrganization(); - - initPackedDatabase(packedDBfile, openMode); + initPackedDatabase(packedDBfile, openMode); // performs upgrade if needed if (openMode == DBConstants.CREATE) { // preserve UniversalID if it has been established @@ -329,8 +346,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * * @param handle database handle * @param addrMap address map (may be null) - * @param openMode open mode CREATE, READ_ONLY or UPDATE (see - * {@link DBConstants}). + * @param openMode open mode CREATE, READ_ONLY or UPDATE (see {@link DBConstants}). + * @param tablePrefix DB table prefix to be applied to all associated table names. This + * need only be specified when using multiple instances with the same + * DB handle (null or empty string for no-prefix). * @param errHandler the error handler * @param lock database lock * @param monitor the current task monitor @@ -340,9 +359,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * This exception will never be thrown in READ_ONLY mode. */ protected DataTypeManagerDB(DBHandle handle, AddressMap addrMap, int openMode, - ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + String tablePrefix, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) throws CancelledException, IOException, VersionException { - + this.tablePrefix = tablePrefix != null ? tablePrefix : ""; this.dbHandle = handle; this.addrMap = addrMap; this.errHandler = errHandler; @@ -352,10 +371,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private void init(int openMode, TaskMonitor monitor) throws CancelledException, IOException, VersionException { + this.mode = openMode; updateID(); initializeAdapters(openMode, monitor); if (checkForSourceArchiveUpdatesNeeded(openMode, monitor)) { - doSourceArchiveUpdates(null, monitor); + doSourceArchiveUpdates(monitor); } dtCache = new DBObjectCache<>(10); sourceArchiveDBCache = new DBObjectCache<>(10); @@ -371,97 +391,110 @@ abstract public class DataTypeManagerDB implements DataTypeManager { throws CancelledException, IOException, VersionException { // - // IMPORTANT! All adapter version must retain read-only capability to permit + // IMPORTANT! All adapter versions must retain read-only capability to permit // opening older archives without requiring an upgrade. Failure to do so may // present severe usability issues when the ability to open for update is not // possible. // - checkAndUpdateManagerVersion(openMode); + checkManagerVersion(openMode); VersionException versionExc = null; try { - builtinAdapter = BuiltinDBAdapter.getAdapter(dbHandle, openMode, monitor); + callingConventionAdapter = + CallingConventionDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - categoryAdapter = CategoryDBAdapter.getAdapter(dbHandle, openMode, monitor); + builtinAdapter = BuiltinDBAdapter.getAdapter(dbHandle, openMode, tablePrefix); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - arrayAdapter = ArrayDBAdapter.getAdapter(dbHandle, openMode, monitor); + categoryAdapter = CategoryDBAdapter.getAdapter(dbHandle, openMode, tablePrefix); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - typedefAdapter = TypedefDBAdapter.getAdapter(dbHandle, openMode, monitor); + arrayAdapter = ArrayDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - compositeAdapter = CompositeDBAdapter.getAdapter(dbHandle, openMode, monitor); + typedefAdapter = TypedefDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - componentAdapter = ComponentDBAdapter.getAdapter(dbHandle, openMode, monitor); + compositeAdapter = + CompositeDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); + } + catch (VersionException e) { + versionExc = e.combine(versionExc); + } + try { + componentAdapter = ComponentDBAdapter.getAdapter(dbHandle, openMode, tablePrefix); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { functionDefAdapter = - FunctionDefinitionDBAdapter.getAdapter(dbHandle, openMode, monitor); + FunctionDefinitionDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, + callingConventionAdapter, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - paramAdapter = FunctionParameterAdapter.getAdapter(dbHandle, openMode, monitor); + paramAdapter = + FunctionParameterAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - settingsAdapter = SettingsDBAdapter.getAdapter(SETTINGS_TABLE_NAME, dbHandle, openMode, + settingsAdapter = + SettingsDBAdapter.getAdapter(tablePrefix + SETTINGS_TABLE_NAME, dbHandle, openMode, null, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - pointerAdapter = PointerDBAdapter.getAdapter(dbHandle, openMode, monitor); + pointerAdapter = PointerDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - enumAdapter = EnumDBAdapter.getAdapter(dbHandle, openMode, monitor); + enumAdapter = EnumDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - enumValueAdapter = EnumValueDBAdapter.getAdapter(dbHandle, openMode, monitor); + enumValueAdapter = + EnumValueDBAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - parentChildAdapter = ParentChildAdapter.getAdapter(dbHandle, openMode, monitor); + parentChildAdapter = ParentChildAdapter.getAdapter(dbHandle, openMode, tablePrefix); } catch (VersionException e) { versionExc = e.combine(versionExc); } try { - sourceArchiveAdapter = SourceArchiveAdapter.getAdapter(dbHandle, openMode, monitor); + sourceArchiveAdapter = + SourceArchiveAdapter.getAdapter(dbHandle, openMode, tablePrefix, monitor); } catch (VersionException e) { versionExc = e.combine(versionExc); @@ -477,6 +510,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (versionExc != null) { throw versionExc; } + + updateManagerAndAppVersion(openMode); } /** @@ -539,20 +574,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager { /** * Check data map for overall manager version for compatibility. - * If not open read-only the map will be immediately updated to latest version. * @throws VersionException if database is a newer unsupported version * @throws IOException if an IO error occurs */ - private void checkAndUpdateManagerVersion(int openMode) throws IOException, VersionException { + private void checkManagerVersion(int openMode) throws IOException, VersionException { if (openMode == DBConstants.CREATE) { - DBStringMapAdapter dataMap = getDataMap(true); - dataMap.put(DTM_DB_VERSION_KEY, Integer.toString(DB_VERSION)); return; } // Check data map for overall manager version for compatibility. - // If not open read-only the map will be immediately updated to latest version. DBStringMapAdapter dataMap = getDataMap(openMode == DBConstants.UPGRADE); if (dataMap != null) { // verify that we are compatible with stored data @@ -560,14 +591,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (dbVersion > DB_VERSION) { throw new VersionException(false); } - if (dbVersion < DB_VERSION) { - if (openMode == DBConstants.UPGRADE) { - // Upgrade mode required to advance overall DB version - dataMap.put(DTM_DB_VERSION_KEY, Integer.toString(DB_VERSION)); - } - else if (openMode == DBConstants.UPDATE) { - throw new VersionException(true); - } + if (dbVersion < DB_VERSION && openMode == DBConstants.UPDATE) { + // Force upgrade if open for update + throw new VersionException(true); } } else if (openMode == DBConstants.UPDATE) { @@ -576,6 +602,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } + private void updateManagerAndAppVersion(int openMode) throws IOException { + if (openMode == DBConstants.CREATE || openMode == DBConstants.UPGRADE) { + DBStringMapAdapter dataMap = getDataMap(true); + dataMap.put(DTM_DB_VERSION_KEY, Integer.toString(DB_VERSION)); + dataMap.put(DTM_GHIDRA_VERSION_KEY, Application.getApplicationVersion()); + } + } + /** * Get the manager string data map. * @param createIfNeeded if true map will be created if it does not exist @@ -736,6 +770,132 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } + /** + * Set the architecture-specific details associated with this datatype manager. + * The data organization will be obtained from the compiler spec specified by + * the program architecture. Fixup of all composites will be performed, if store is + * true, to reflect any changes in the data organization. + * The caller is resposible for ensuring that this setting is done consistent + * with the {@link #addrMap} setting used during construction if applicable. + * @param programArchitecture program architecture details (may be null) in which case + * default data organization will be used. + * @param variableStorageMgr variable storage manager (within same database) or null + * to disable variable storage support. Ignored if programArchitecture is null; + * @param store if true database update will occur and datatypes will be updated if + * any change to the data organization is detected (a stored copy may be used to + * detect this condition). This should never be passed as true if opened read-only. + * @param monitor task monitor + * @throws IOException if IO error occurs + * @throws CancelledException if processing cancelled - data types may not properly reflect + * updated compiler specification + * @throws UnsupportedOperationException if language was previously set + */ + protected void setProgramArchitecture(ProgramArchitecture programArchitecture, + VariableStorageManager variableStorageMgr, boolean store, TaskMonitor monitor) + throws IOException, CancelledException { + + this.programArchitecture = programArchitecture; + this.variableStorageMgr = programArchitecture != null ? variableStorageMgr : null; + + dataOrganization = programArchitecture != null + ? programArchitecture.getCompilerSpec().getDataOrganization() + : DataOrganizationImpl.getDefaultOrganization(); + + if (mode == DBConstants.CREATE) { + saveDataOrganization(); + } + else if (store) { + compilerSpecChanged(monitor); + updateLastChangeTime(); + } + } + + /** + * Perform updates related to a compiler spec change, including: + *

    + *
  • data organization changes which may impact datatype components and packing
  • + *
+ * NOTE: this manager must be open for update. + * @param monitor task monitor + * @throws ReadOnlyException if this manager has not been open for update + * @throws IOException if an IO error occurs while performing updates + * @throws CancelledException if processing cancelled - data types may not properly reflect + * updated compiler specification + */ + private void compilerSpecChanged(TaskMonitor monitor) throws IOException, CancelledException { + + if (mode == DBConstants.READ_ONLY) { + throw new ReadOnlyException(); + } + + DataOrganization oldDataOrganization = readDataOrganization(); + + try { + saveDataOrganization(); + + if (oldDataOrganization != null && + !oldDataOrganization.equals(dataOrganization)) { + Msg.info(this, + "Fixing datatypes to reflect data organization change: " + getPath()); + doCompositeFixup(monitor); + } + + // FUTURE: may need to handle calling convention and data organization change impact + // on function definitions + + } + finally { + invalidateCache(); + } + } + + private void saveDataOrganization() throws IOException { + if (dataOrganization == null) { + return; + } + DataOrganizationImpl.save(dataOrganization, getDataMap(true), "dataOrg."); + } + + private DataOrganization readDataOrganization() throws IOException { + DBStringMapAdapter dataMap = getDataMap(false); + if (dataMap == null) { + return null; + } + return DataOrganizationImpl.restore(dataMap, "dataOrg."); + } + + @Override + public ProgramArchitecture getProgramArchitecture() { + return programArchitecture; + } + + protected static String getProgramArchitectureSummary(LanguageID languageId, + int languageVersion, CompilerSpecID compilerSpecId) { + StringBuilder buf = new StringBuilder(); + buf.append(languageId.getIdAsString()); + buf.append(" / "); + buf.append(compilerSpecId.getIdAsString()); + return buf.toString(); + } + + @Override + public String getProgramArchitectureSummary() { + if (programArchitecture != null) { + return getProgramArchitectureSummary(programArchitecture.getLanguage().getLanguageID(), + programArchitecture.getLanguage().getVersion(), + programArchitecture.getCompilerSpec().getCompilerSpecID()); + } + return null; + } + + /** + * Get the variable storage manager if it has been established. + * @return variable storage manager or null if no associated architecture. + */ + protected VariableStorageManager getVariableStorageManager() { + return variableStorageMgr; + } + /** * Determine if transaction is active. With proper lock established * this method may be useful for determining if a lazy record update @@ -1130,7 +1290,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { int storageSize = bitFieldDataType.getStorageSize(); int storageSizeBits = 8 * storageSize; if ((bitOffset + bitSize) > storageSizeBits) { - // should get recomputed during packing when used within aligned structure + // should get recomputed during packing when used within structure with packing enabled int effectiveBitSize = Math.min(bitSize, baseLengthBits); bitOffset = getDataOrganization().isBigEndian() ? baseLengthBits - effectiveBitSize : 0; storageSize = baseLength; @@ -1574,11 +1734,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (sourceArchive == null) { return null; } - if (getSourceArchive(sourceArchive.getSourceArchiveID()) != null) { - // already have it - return getSourceArchive(sourceArchive.getSourceArchiveID()); - } + lock.acquire(); try { + SourceArchive existingArchive = getSourceArchive(sourceArchive.getSourceArchiveID()); + if (existingArchive != null) { + return existingArchive; // already have it + } DBRecord record = sourceArchiveAdapter.createRecord(sourceArchive); SourceArchive newSourceArchive = getSourceArchiveDB(record); invalidateSourceArchiveCache(); @@ -1587,8 +1748,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } catch (IOException e) { dbError(e); - return null; } + finally { + lock.release(); + } + return null; } @Override @@ -1602,15 +1766,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager { throw new IllegalArgumentException("Attempted to delete the local archive!"); } disassociateAllDataTypes(sourceArchiveID); - try { - sourceArchiveAdapter.deleteRecord(sourceArchiveID); - } - catch (IOException e) { - dbError(e); - } - sourceArchiveChanged(sourceArchiveID); + sourceArchiveAdapter.deleteRecord(sourceArchiveID); + sourceArchiveChanged(sourceArchiveID); // must occur before invalidateSourceArchiveCache invalidateSourceArchiveCache(); } + catch (IOException e) { + dbError(e); + } finally { lock.release(); } @@ -1747,6 +1909,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } + /** + * Replace all datatype uses external to the datatype manager if applicable. + * @param oldID old datatype ID + * @param newID new datatype ID + */ abstract protected void replaceDataTypeIDs(long oldID, long newID); /** @@ -2167,6 +2334,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { idsToDelete.add(Long.valueOf(id)); } + /** + * Delete all datatype uses external to the datatype manager if applicable. + * @param deletedIds old datatype IDs which were deleted + * @param monitor task monitor + * @throws CancelledException if operation cancelled + */ abstract protected void deleteDataTypeIDs(LinkedList deletedIds, TaskMonitor monitor) throws CancelledException; @@ -2442,26 +2615,36 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private DataType getDataType(long dataTypeID, DBRecord record) { int tableId = getTableID(dataTypeID); + DataType dt = null; switch (tableId) { case BUILT_IN: - return getBuiltInDataType(dataTypeID, record); + dt = getBuiltInDataType(dataTypeID, record); + break; case COMPOSITE: - return getCompositeDataType(dataTypeID, record); + dt = getCompositeDataType(dataTypeID, record); + break; case ARRAY: - return getArrayDataType(dataTypeID, record); + dt = getArrayDataType(dataTypeID, record); + break; case POINTER: - return getPointerDataType(dataTypeID, record); + dt = getPointerDataType(dataTypeID, record); + break; case TYPEDEF: - return getTypedefDataType(dataTypeID, record); + dt = getTypedefDataType(dataTypeID, record); + break; case FUNCTION_DEF: - return getFunctionDefDataType(dataTypeID, record); + dt = getFunctionDefDataType(dataTypeID, record); + break; case ENUM: - return getEnumDataType(dataTypeID, record); + dt = getEnumDataType(dataTypeID, record); + break; case BITFIELD: - return BitFieldDBDataType.getBitFieldDataType(dataTypeID, this); + dt = BitFieldDBDataType.getBitFieldDataType(dataTypeID, this); + break; default: return null; } + return dt; } private DataType getBuiltInDataType(long dataTypeID, DBRecord record) { @@ -2938,7 +3121,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return arrayDB; } - private void updateLastChangeTime() { + protected void updateLastChangeTime() { SourceArchive mySourceArchive = getSourceArchive(getUniversalID()); if (mySourceArchive == null) { return; @@ -3105,10 +3288,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } try { creatingDataType++; + byte callingConventionId = + callingConventionAdapter.getCallingConventionId(funDef.getCallingConventionName(), + cc -> callingConventionNameAdded(cc)); DBRecord record = functionDefAdapter.createRecord(name, funDef.getComment(), cat.getID(), - DEFAULT_DATATYPE_ID, funDef.hasVarArgs(), funDef.getGenericCallingConvention(), - sourceArchiveIdValue, universalIdValue, funDef.getLastChangeTime()); + DEFAULT_DATATYPE_ID, funDef.hasNoReturn(), funDef.hasVarArgs(), + callingConventionId, sourceArchiveIdValue, universalIdValue, + funDef.getLastChangeTime()); FunctionDefinitionDB funDefDb = new FunctionDefinitionDB(this, dtCache, functionDefAdapter, paramAdapter, record); @@ -3127,13 +3314,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager { creatingDataType--; } } - - class StructureIterator implements Iterator { + + class DataTypeIterator implements Iterator { private RecordIterator it; - private StructureDB nextStruct; + private T nextDataType; + private Predicate predicate; - StructureIterator() throws IOException { - it = compositeAdapter.getRecords(); + DataTypeIterator(DBRecordAdapter adapter, Predicate predicate) + throws IOException { + it = adapter.getRecords(); + this.predicate = predicate; } @Override @@ -3143,29 +3333,30 @@ abstract public class DataTypeManagerDB implements DataTypeManager { @Override public boolean hasNext() { - if (nextStruct == null) { - getNextStruct(); + if (nextDataType == null) { + getNextDataType(); } - return nextStruct != null; + return nextDataType != null; } @Override - public StructureDB next() { + public T next() { if (hasNext()) { - StructureDB s = nextStruct; - nextStruct = null; - return s; + T dt = nextDataType; + nextDataType = null; + return dt; } return null; } - private void getNextStruct() { + @SuppressWarnings("unchecked") + private void getNextDataType() { try { while (it.hasNext()) { DBRecord rec = it.next(); DataType dt = getDataType(rec.getKey(), rec); - if (dt instanceof Structure) { - nextStruct = (StructureDB) dt; + if (predicate.test(dt)) { + nextDataType = (T) dt; return; } } @@ -3176,50 +3367,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } - class CompositeIterator implements Iterator { - private RecordIterator it; - private CompositeDB nextComposite; - - CompositeIterator() throws IOException { - it = compositeAdapter.getRecords(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Remove not supported"); - } - - @Override - public boolean hasNext() { - if (nextComposite == null) { - getNextComposite(); - } - return nextComposite != null; - } - - @Override - public CompositeDB next() { - if (hasNext()) { - CompositeDB c = nextComposite; - nextComposite = null; - return c; - } - return null; - } - - private void getNextComposite() { - try { - if (it.hasNext()) { - DBRecord rec = it.next(); - nextComposite = (CompositeDB) getDataType(rec.getKey(), rec); - } - } - catch (IOException e) { - Msg.error(this, "Unexpected exception iterating composites", e); - } - } - } - private class NameComparator implements Comparator { /** * Compares its two arguments for order. Returns a negative integer, zero, or a @@ -3321,10 +3468,23 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } + @Override + public Iterator getAllFunctionDefinitions() { + try { + return new DataTypeIterator(functionDefAdapter, dt -> true); + } + catch (IOException e) { + dbError(e); + } + List emptyList = List.of(); + return emptyList.iterator(); + } + @Override public Iterator getAllStructures() { try { - return new StructureIterator(); + return new DataTypeIterator(compositeAdapter, + dt -> (dt instanceof Structure)); } catch (IOException e) { dbError(e); @@ -3335,7 +3495,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { @Override public Iterator getAllComposites() { try { - return new CompositeIterator(); + return new DataTypeIterator(compositeAdapter, dt -> true); } catch (IOException e) { dbError(e); @@ -3359,6 +3519,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { public void invalidateCache() { lock.acquire(); try { + callingConventionAdapter.invalidateCache(); + knownCallingConventions = null; + definedCallingConventions = null; dtCache.invalidate(); sourceArchiveDBCache.invalidate(); invalidateSourceArchiveCache(); @@ -3540,12 +3703,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } @Override - public Pointer getPointer(DataType dt) { + public final Pointer getPointer(DataType dt) { return new PointerDataType(dt, this); } @Override - public Pointer getPointer(DataType dt, int size) { + public final Pointer getPointer(DataType dt, int size) { return new PointerDataType(dt, size, this); } @@ -3576,7 +3739,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { if (dt instanceof Enum) { enumValueMap = null; } - if (creatingDataType == 0) { + if (!isAutoChange && creatingDataType == 0) { + // auto-changes should not be synced updateLastChangeTime(); setDirtyFlag(dt); } @@ -3762,13 +3926,192 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } @Override - public DataOrganization getDataOrganization() { + public AddressMap getAddressMap() { + return addrMap; + } + + @Override + public final DataOrganization getDataOrganization() { if (dataOrganization == null) { dataOrganization = DataOrganizationImpl.getDefaultOrganization(); } return dataOrganization; } + /** + * Get calling convention name corresponding to existing specified id. + * @param id calling convention ID + * @return calling convention name if found else unknown + */ + public String getCallingConventionName(byte id) { + lock.acquire(); + try { + String callingConvention = callingConventionAdapter.getCallingConventionName(id); + return callingConvention != null ? callingConvention + : CompilerSpec.CALLING_CONVENTION_unknown; + } + catch (IOException e) { + dbError(e); + } + finally { + lock.release(); + } + return null; + } + + /** + * Get (and assign if needed thus requiring open transaction) the ID associated with the + * specified calling convention name. If name is a new convention and the number of stored + * convention names exceeds 127 the returned ID will correspond to the unknown calling + * convention. + * @param name calling convention name + * @param restrictive if true an error will be thrown if name is not defined by + * {@link GenericCallingConvention} or the associated compiler specification if + * datatype manager has an associated program architecture. + * @return calling convention ID + * @throws IOException if database IO error occurs + * @throws InvalidInputException if restrictive is true and name is not defined by + * {@link GenericCallingConvention} or the associated compiler specification if + * datatype manager has an associated program architecture. + */ + public byte getCallingConventionID(String name, boolean restrictive) + throws InvalidInputException, IOException { + + if (name == null || CompilerSpec.CALLING_CONVENTION_unknown.equals(name)) { + return UNKNOWN_CALLING_CONVENTION_ID; + } + if (CompilerSpec.CALLING_CONVENTION_default.equals(name)) { + return DEFAULT_CALLING_CONVENTION_ID; + } + + lock.acquire(); + try { + // If restrictive only permit a name which is known + if (restrictive && + GenericCallingConvention + .getGenericCallingConvention(name) == GenericCallingConvention.unknown && + !getKnownCallingConventionNames().contains(name) && + getCallingConvention(name) == null) { + + throw new InvalidInputException("Invalid calling convention name: " + name); + } + + return callingConventionAdapter.getCallingConventionId(name, + cc -> callingConventionNameAdded(cc)); + } + catch (IOException e) { + dbError(e); + } + finally { + lock.release(); + } + return UNKNOWN_CALLING_CONVENTION_ID; + } + + private void callingConventionNameAdded(String name) { + getKnownCallingConventionSet().add(name); + } + + private Set getDefinedCallingConventionSet() { + if (definedCallingConventions == null) { + definedCallingConventions = buildDefinedCallingConventionSet(); + } + return definedCallingConventions; + } + + private TreeSet buildDefinedCallingConventionSet() { + + // Include all calling conventions defined by associated architecure compiler spec + TreeSet nameSet = new TreeSet<>(); + ProgramArchitecture arch = getProgramArchitecture(); + if (arch != null) { + CompilerSpec compilerSpec = arch.getCompilerSpec(); + PrototypeModel[] namedCallingConventions = compilerSpec.getCallingConventions(); + for (PrototypeModel model : namedCallingConventions) { + nameSet.add(model.getName()); + } + } + + // Include all generic calling convention names without cspec + else { + for (GenericCallingConvention conv : GenericCallingConvention.values()) { + if (conv == GenericCallingConvention.unknown) { + continue; // added below + } + nameSet.add(conv.getDeclarationName()); + } + } + + return nameSet; + } + + @Override + public Collection getDefinedCallingConventionNames() { + lock.acquire(); + try { + return new ArrayList<>(getDefinedCallingConventionSet()); + } + finally { + lock.release(); + } + } + + private Set getKnownCallingConventionSet() { + if (knownCallingConventions == null) { + knownCallingConventions = buildKnownCallingConventionSet(); + } + return knownCallingConventions; + } + + private TreeSet buildKnownCallingConventionSet() { + TreeSet nameSet = new TreeSet<>(); + try { + // Include defined call convention names + nameSet.addAll(getDefinedCallingConventionSet()); + + // Include all calling convention names previously added to DB + for (String name : callingConventionAdapter.getCallingConventionNames()) { + nameSet.add(name); + } + } + catch (IOException e) { + dbError(e); + } + + return nameSet; + } + + @Override + public Collection getKnownCallingConventionNames() { + lock.acquire(); + try { + return new ArrayList<>(getKnownCallingConventionSet()); + } + finally { + lock.release(); + } + } + + @Override + public PrototypeModel getDefaultCallingConvention() { + ProgramArchitecture arch = getProgramArchitecture(); + if (arch != null) { + CompilerSpec compilerSpec = arch.getCompilerSpec(); + return compilerSpec.getDefaultCallingConvention(); + } + return null; + } + + @Override + public PrototypeModel getCallingConvention(String name) { + ProgramArchitecture arch = getProgramArchitecture(); + if (arch != null) { + CompilerSpec compilerSpec = arch.getCompilerSpec(); + return compilerSpec.getCallingConvention(name); + } + return null; + } + private boolean checkForSourceArchiveUpdatesNeeded(int openMode, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == DBConstants.CREATE || openMode == DBConstants.READ_ONLY) { @@ -3816,16 +4159,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager { /** * This method is only invoked during an upgrade. * - * @param compilerSpec compiler spec * @param monitor task monitor * @throws CancelledException if task cancelled */ - protected void doSourceArchiveUpdates(CompilerSpec compilerSpec, TaskMonitor monitor) + protected void doSourceArchiveUpdates(TaskMonitor monitor) throws CancelledException { SourceArchiveUpgradeMap upgradeMap = new SourceArchiveUpgradeMap(); for (SourceArchive sourceArchive : getSourceArchives()) { SourceArchive mappedSourceArchive = - upgradeMap.getMappedSourceArchive(sourceArchive, compilerSpec); + upgradeMap.getMappedSourceArchive(sourceArchive); if (mappedSourceArchive != null) { replaceSourceArchive(sourceArchive, mappedSourceArchive); } @@ -3854,36 +4196,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * that this program be open with exclusive access before invoking this method to avoid * excessive merge conflicts with other users. * @param monitor task monitor - * @throws CancelledException if operation is cancelled + * @throws CancelledException if processing cancelled - data types may not properly reflect + * updated compiler specification */ public void fixupComposites(TaskMonitor monitor) throws CancelledException { lock.acquire(); try { - - // NOTE: Any composite could be indirectly affected by a component size change - // based upon type relationships - - // NOTE: Composites brought in from archive may have incorrect component size - // if not aligned and should not be used to guage a primitive size change - - // Unfortunately parent table does not track use of primitives so a brute - // force search is required. Since all composites must be checked, this - // is combined with the composite graph generation to get ordered list - // of composites for subsequent size change operation. - - List orderedComposites = getAllCompositesInPostDependencyOrder(monitor); - - monitor.setProgress(0); - monitor.setMaximum(orderedComposites.size()); - monitor.setMessage("Updating Datatype Sizes..."); - - int count = 0; - for (CompositeDB c : orderedComposites) { - monitor.checkCanceled(); - c.fixupComponents(); - monitor.setProgress(++count); - } - + doCompositeFixup(monitor); } catch (IOException e) { dbError(e); @@ -3892,6 +4211,34 @@ abstract public class DataTypeManagerDB implements DataTypeManager { lock.release(); } } + + private void doCompositeFixup(TaskMonitor monitor) throws CancelledException, IOException { + + // NOTE: Any composite could be indirectly affected by a component size change + // based upon type relationships. In addition, an architecture change could + // alter the size and alignment of any datatype. + + // NOTE: Composites brought in from archive may have incorrect component size + // if packing disabled and should not be used to guage a primitive size change + + // Unfortunately parent table does not track use of primitives so a brute + // force search is required. Since all composites must be checked, this + // is combined with the composite graph generation to get ordered list + // of composites for subsequent size change operation. + + List orderedComposites = getAllCompositesInPostDependencyOrder(monitor); + + monitor.setProgress(0); + monitor.setMaximum(orderedComposites.size()); + monitor.setMessage("Updating Datatype Sizes..."); + + int count = 0; + for (CompositeDB c : orderedComposites) { + monitor.checkCanceled(); + c.fixupComponents(); + monitor.setProgress(++count); + } + } /** * Get composite base type which corresponds to a specified datatype. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java index 1644fa4a7c..8a5e73d441 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java @@ -19,6 +19,7 @@ import java.io.IOException; import db.*; import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -45,26 +46,26 @@ abstract class EnumDBAdapter { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of enumeration data types. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task cancelled */ - static EnumDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + static EnumDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { if (openMode == DBConstants.CREATE) { - return new EnumDBAdapterV1(handle, true); + return new EnumDBAdapterV1(handle, tablePrefix, true); } try { - return new EnumDBAdapterV1(handle, false); + return new EnumDBAdapterV1(handle, tablePrefix, false); } catch (VersionException e) { -// if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { -// throw e; -// } EnumDBAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } @@ -76,7 +77,7 @@ abstract class EnumDBAdapter { * @return the read only Enumeration data type table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ - static EnumDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { + private static EnumDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { try { return new EnumDBAdapterV0(handle); } @@ -93,28 +94,35 @@ abstract class EnumDBAdapter { * Upgrades the Enumeration data type table from the oldAdapter's version to the current version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor * @return the adapter for the new upgraded version of the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if the database can't be read or written. + * @throws CancelledException if task cancelled */ - static EnumDBAdapter upgrade(DBHandle handle, EnumDBAdapter oldAdapter) - throws VersionException, IOException { + private static EnumDBAdapter upgrade(DBHandle handle, EnumDBAdapter oldAdapter, + String tablePrefix, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); EnumDBAdapter tmpAdapter = null; try { - tmpAdapter = new EnumDBAdapterV1(tmpHandle, true); + tmpAdapter = new EnumDBAdapterV1(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec, false); } oldAdapter.deleteTable(handle); - EnumDBAdapterV1 newAdapter = new EnumDBAdapterV1(handle, true); + EnumDBAdapterV1 newAdapter = new EnumDBAdapterV1(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec, false); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java index 733b9a9708..3bcd715bfd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java @@ -28,6 +28,7 @@ import ghidra.util.exception.VersionException; * Version 0 implementation for accessing the Enumeration database table. */ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { + static final int VERSION = 0; // Enum Columns @@ -36,10 +37,11 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { static final int V0_ENUM_CAT_COL = 2; static final int V0_ENUM_SIZE_COL = 3; - static final Schema V0_ENUM_SCHEMA = new Schema( - VERSION, "Enum ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE, - LongField.INSTANCE, ByteField.INSTANCE }, - new String[] { "Name", "Comment", "Category ID", "Size" }); +// DO NOT REMOVE - this documents the schema used in version 0. +// static final Schema V0_ENUM_SCHEMA = new Schema( +// VERSION, "Enum ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE, +// LongField.INSTANCE, ByteField.INSTANCE }, +// new String[] { "Name", "Comment", "Category ID", "Size" }); private Table enumTable; @@ -53,16 +55,11 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { enumTable = handle.getTable(ENUM_TABLE_NAME); if (enumTable == null) { - throw new VersionException("Missing Table: " + ENUM_TABLE_NAME); + throw new VersionException(true); } int version = enumTable.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + ENUM_TABLE_NAME + - " but got " + enumTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(false); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java index aa37beae15..06b3e7ce1b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java @@ -24,6 +24,8 @@ import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Enumeration database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. */ class EnumDBAdapterV1 extends EnumDBAdapter { static final int VERSION = 1; @@ -50,29 +52,27 @@ class EnumDBAdapterV1 extends EnumDBAdapter { /** * Gets a version 1 adapter for the Enumeration database table. * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. + * @throws IOException an IO error occured during table creation */ - public EnumDBAdapterV1(DBHandle handle, boolean create) throws VersionException, IOException { - + public EnumDBAdapterV1(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + ENUM_TABLE_NAME; if (create) { - enumTable = handle.createTable(ENUM_TABLE_NAME, V1_ENUM_SCHEMA, + enumTable = handle.createTable(tableName, V1_ENUM_SCHEMA, new int[] { V1_ENUM_CAT_COL, V1_ENUM_UNIVERSAL_DT_ID_COL }); } else { - enumTable = handle.getTable(ENUM_TABLE_NAME); + enumTable = handle.getTable(tableName); if (enumTable == null) { - throw new VersionException("Missing Table: " + ENUM_TABLE_NAME); + throw new VersionException(true); } int version = enumTable.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + ENUM_TABLE_NAME + - " but got " + enumTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } } @@ -121,7 +121,7 @@ class EnumDBAdapterV1 extends EnumDBAdapter { @Override protected void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(ENUM_TABLE_NAME); + handle.deleteTable(enumTable.getName()); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java index bb0adfeba2..1b0d8403d6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java @@ -24,6 +24,7 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -46,18 +47,20 @@ abstract class EnumValueDBAdapter implements RecordTranslator { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of enumeration data type values. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task is cancelled */ - static EnumValueDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + static EnumValueDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { if (openMode == DBConstants.CREATE) { - return new EnumValueDBAdapterV1(handle, true); + return new EnumValueDBAdapterV1(handle, tablePrefix, true); } try { - return new EnumValueDBAdapterV1(handle, false); + return new EnumValueDBAdapterV1(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -65,13 +68,13 @@ abstract class EnumValueDBAdapter implements RecordTranslator { } EnumValueDBAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } } - static EnumValueDBAdapter findReadOnlyAdapter(DBHandle handle) { + private static EnumValueDBAdapter findReadOnlyAdapter(DBHandle handle) { try { return new EnumValueDBAdapterV0(handle); } @@ -85,28 +88,34 @@ abstract class EnumValueDBAdapter implements RecordTranslator { * version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor * @return the adapter for the new upgraded version of the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if the database can't be read or written. + * @throws CancelledException if task is cancelled */ - static EnumValueDBAdapter upgrade(DBHandle handle, EnumValueDBAdapter oldAdapter) - throws VersionException, IOException { + private static EnumValueDBAdapter upgrade(DBHandle handle, EnumValueDBAdapter oldAdapter, + String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); EnumValueDBAdapter tmpAdapter = null; try { - tmpAdapter = new EnumValueDBAdapterV1(tmpHandle, true); + tmpAdapter = new EnumValueDBAdapterV1(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec); } oldAdapter.deleteTable(handle); - EnumValueDBAdapter newAdapter = new EnumValueDBAdapterV1(handle, true); + EnumValueDBAdapter newAdapter = new EnumValueDBAdapterV1(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec); } @@ -149,9 +158,7 @@ abstract class EnumValueDBAdapter implements RecordTranslator { * @param handle the handle used to delete the table * @throws IOException if there was a problem accessing the database */ - void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(ENUM_VALUE_TABLE_NAME); - } + abstract void deleteTable(DBHandle handle) throws IOException; /** * Remove the record for the given enum Value ID. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java index a07ff31f4e..8afa4fb1df 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java @@ -32,33 +32,33 @@ class EnumValueDBAdapterNoTable extends EnumValueDBAdapter { * Gets a pre-table version of the adapter for the enumeration data type values database table. * @param handle handle to the database which doesn't contain the table. */ - public EnumValueDBAdapterNoTable(DBHandle handle) { + EnumValueDBAdapterNoTable(DBHandle handle) { // no table needed } @Override - public void createRecord(long enumID, String name, long value, String comment) + void createRecord(long enumID, String name, long value, String comment) throws IOException { throw new UnsupportedOperationException(); } @Override - public DBRecord getRecord(long valueID) throws IOException { + DBRecord getRecord(long valueID) throws IOException { return null; } @Override - public void updateRecord(DBRecord record) throws IOException { + void updateRecord(DBRecord record) throws IOException { throw new UnsupportedOperationException(); } @Override - public void removeRecord(long valueID) throws IOException { + void removeRecord(long valueID) throws IOException { throw new UnsupportedOperationException(); } @Override - public Field[] getValueIdsInEnum(long enumID) throws IOException { + Field[] getValueIdsInEnum(long enumID) throws IOException { return Field.EMPTY_ARRAY; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java index 87dfa5fd83..68226355e4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java @@ -39,7 +39,7 @@ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public EnumValueDBAdapterV0(DBHandle handle) throws VersionException { + EnumValueDBAdapterV0(DBHandle handle) throws VersionException { table = handle.getTable(ENUM_VALUE_TABLE_NAME); if (table == null) { @@ -55,31 +55,35 @@ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { } @Override - public void createRecord(long enumID, String name, long value, String comment) + void createRecord(long enumID, String name, long value, String comment) throws IOException { throw new UnsupportedOperationException("Cannot update Version 0"); } @Override - public DBRecord getRecord(long valueID) throws IOException { + DBRecord getRecord(long valueID) throws IOException { return translateRecord(table.getRecord(valueID)); } @Override - public void removeRecord(long valueID) throws IOException { + void removeRecord(long valueID) throws IOException { throw new UnsupportedOperationException("Cannot remove Version 0"); } @Override - public void updateRecord(DBRecord record) throws IOException { + void updateRecord(DBRecord record) throws IOException { throw new UnsupportedOperationException("Cannot update Version 0"); } @Override - public Field[] getValueIdsInEnum(long enumID) throws IOException { + Field[] getValueIdsInEnum(long enumID) throws IOException { return table.findRecords(new LongField(enumID), ENUMVAL_ID_COL); } + void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(ENUM_VALUE_TABLE_NAME); + } + @Override public DBRecord translateRecord(DBRecord oldRec) { if (oldRec == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java index 1b3b922767..33522350c3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java @@ -22,6 +22,8 @@ import ghidra.util.exception.VersionException; /** * Version 1 implementation for the enumeration tables adapter. + * + * NOTE: Use of tablePrefix introduced with this adapter version. */ class EnumValueDBAdapterV1 extends EnumValueDBAdapter { @@ -36,36 +38,32 @@ class EnumValueDBAdapterV1 extends EnumValueDBAdapter { /** * Gets a version 1 adapter for the Enumeration Data Type Values database table. * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if IO error occurs */ - public EnumValueDBAdapterV1(DBHandle handle, boolean create) + EnumValueDBAdapterV1(DBHandle handle, String tablePrefix, boolean create) throws VersionException, IOException { - + String tableName = tablePrefix + ENUM_VALUE_TABLE_NAME; if (create) { - table = handle.createTable(ENUM_VALUE_TABLE_NAME, SCHEMA, new int[] { ENUMVAL_ID_COL }); + table = handle.createTable(tableName, SCHEMA, new int[] { ENUMVAL_ID_COL }); } else { - table = handle.getTable(ENUM_VALUE_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { throw new VersionException(true); } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } } @Override - public void createRecord(long enumID, String name, long value, String comment) + void createRecord(long enumID, String name, long value, String comment) throws IOException { DBRecord record = SCHEMA.createRecord(table.getKey()); record.setLongValue(ENUMVAL_ID_COL, enumID); @@ -76,22 +74,22 @@ class EnumValueDBAdapterV1 extends EnumValueDBAdapter { } @Override - public DBRecord getRecord(long valueID) throws IOException { + DBRecord getRecord(long valueID) throws IOException { return table.getRecord(valueID); } @Override - public void removeRecord(long valueID) throws IOException { + void removeRecord(long valueID) throws IOException { table.deleteRecord(valueID); } @Override - public void updateRecord(DBRecord record) throws IOException { + void updateRecord(DBRecord record) throws IOException { table.putRecord(record); } @Override - public Field[] getValueIdsInEnum(long enumID) throws IOException { + Field[] getValueIdsInEnum(long enumID) throws IOException { return table.findRecords(new LongField(enumID), ENUMVAL_ID_COL); } @@ -100,6 +98,10 @@ class EnumValueDBAdapterV1 extends EnumValueDBAdapter { return table.iterator(); } + void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(table.getName()); + } + @Override public DBRecord translateRecord(DBRecord r) { return r; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java index b0fdce3a9b..9c1ccb91d2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java @@ -24,12 +24,14 @@ import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsImpl; import ghidra.program.database.DBObjectCache; import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.FunctionSignature; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.symbol.SourceType; -import ghidra.util.Msg; import ghidra.util.UniversalID; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.InvalidInputException; class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { @@ -108,13 +110,17 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { try { checkIsValid(); StringBuffer buf = new StringBuffer(); + if (includeCallingConvention && hasNoReturn()) { + buf.append(NORETURN_DISPLAY_STRING); + buf.append(" "); + } DataType returnType = getReturnType(); buf.append((returnType != null ? returnType.getDisplayName() : "void")); buf.append(" "); if (includeCallingConvention) { - GenericCallingConvention genericCallingConvention = getGenericCallingConvention(); - if (genericCallingConvention != GenericCallingConvention.unknown) { - buf.append(genericCallingConvention.name()); + String callingConvention = getCallingConventionName(); + if (!Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(callingConvention)) { + buf.append(callingConvention); buf.append(" "); } } @@ -188,7 +194,6 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { lock.acquire(); try { checkDeleted(); - setArguments(functionDefinition.getArguments()); try { setReturnType(functionDefinition.getReturnType()); @@ -197,7 +202,16 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { setReturnType(DEFAULT); } setVarArgs(functionDefinition.hasVarArgs()); - setGenericCallingConvention(functionDefinition.getGenericCallingConvention()); + setNoReturn(functionDefinition.hasNoReturn()); + try { + setCallingConvention(functionDefinition.getCallingConventionName(), false); + } + catch (InvalidInputException e) { + // will not happen + } + } + catch (IOException e) { + dataMgr.dbError(e); } finally { lock.release(); @@ -394,8 +408,9 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { (comment != null && comment.equals(myComment))) && (DataTypeUtilities.isSameOrEquivalentDataType(getReturnType(), signature.getReturnType())) && - (getGenericCallingConvention() == signature.getGenericCallingConvention()) && - (hasVarArgs() == signature.hasVarArgs())) { + getCallingConventionName().equals(signature.getCallingConventionName()) && + (hasVarArgs() == signature.hasVarArgs()) && + (hasNoReturn() == signature.hasNoReturn())) { ParameterDefinition[] args = signature.getArguments(); ParameterDefinition[] thisArgs = this.getArguments(); if (args.length == thisArgs.length) { @@ -517,6 +532,22 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { } } + @Override + public boolean hasNoReturn() { + lock.acquire(); + try { + checkIsValid(); + if (record == null) { + return false; + } + byte flags = record.getByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_FLAGS_COL); + return ((flags & FunctionDefinitionDBAdapter.FUNCTION_DEF_NORETURN_FLAG) != 0); + } + finally { + lock.release(); + } + } + @Override public void setVarArgs(boolean hasVarArgs) { lock.acquire(); @@ -544,20 +575,17 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { } @Override - public void setGenericCallingConvention(GenericCallingConvention genericCallingConvention) { + public void setNoReturn(boolean hasNoReturn) { lock.acquire(); try { checkDeleted(); - int ordinal = genericCallingConvention.ordinal(); - if (ordinal < 0 || - ordinal > FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_MASK) { - Msg.error(this, "GenericCallingConvention ordinal unsupported: " + ordinal); - return; - } byte flags = record.getByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_FLAGS_COL); - flags &= - ~(FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_MASK << FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_SHIFT); - flags |= ordinal << FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_SHIFT; + if (hasNoReturn) { + flags |= FunctionDefinitionDBAdapter.FUNCTION_DEF_NORETURN_FLAG; + } + else { + flags &= ~FunctionDefinitionDBAdapter.FUNCTION_DEF_NORETURN_FLAG; + } record.setByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_FLAGS_COL, flags); try { funDefAdapter.updateRecord(record, true); @@ -573,18 +601,70 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { } @Override - public GenericCallingConvention getGenericCallingConvention() { + public void setGenericCallingConvention(GenericCallingConvention genericCallingConvention) { + lock.acquire(); + try { + checkDeleted(); + setCallingConvention(genericCallingConvention.name(), false); + } + catch (IOException e) { + dataMgr.dbError(e); + } + catch (InvalidInputException e) { + throw new AssertException(e); + } + finally { + lock.release(); + } + } + + @Override + public void setCallingConvention(String conventionName) throws InvalidInputException { + lock.acquire(); + try { + checkDeleted(); + setCallingConvention(conventionName, true); + } + catch (IOException e) { + dataMgr.dbError(e); + } + finally { + lock.release(); + } + } + + private void setCallingConvention(String conventionName, boolean restrictive) + throws InvalidInputException, IOException { + byte id = dataMgr.getCallingConventionID(conventionName, restrictive); + record.setByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_CALLCONV_COL, id); + funDefAdapter.updateRecord(record, true); + dataMgr.dataTypeChanged(this, false); + } + + @Override + public PrototypeModel getCallingConvention() { + ProgramArchitecture arch = dataMgr.getProgramArchitecture(); + if (arch == null) { + return null; + } + String callingConvention = getCallingConventionName(); + CompilerSpec compilerSpec = arch.getCompilerSpec(); + return compilerSpec.getCallingConvention(callingConvention); + } + + @Override + public String getCallingConventionName() { lock.acquire(); try { checkIsValid(); if (record == null) { - return GenericCallingConvention.unknown; + return Function.UNKNOWN_CALLING_CONVENTION_STRING; } - byte flags = record.getByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_FLAGS_COL); - int ordinal = - (flags >> FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_SHIFT) & - FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_MASK; - return GenericCallingConvention.get(ordinal); + byte id = record.getByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_CALLCONV_COL); + if (funDefAdapter.usesGenericCallingConventionId()) { + return FunctionDefinitionDBAdapter.getGenericCallingConventionName(id); + } + return dataMgr.getCallingConventionName(id); } finally { lock.release(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java index cefe1f96bb..d840224fac 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java @@ -18,68 +18,81 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.program.database.util.DBRecordAdapter; import ghidra.program.model.data.GenericCallingConvention; +import ghidra.program.model.lang.CompilerSpec; import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; /** * Adapter to access the Function Signature Definition database table. */ -abstract class FunctionDefinitionDBAdapter { +abstract class FunctionDefinitionDBAdapter implements DBRecordAdapter { static final String FUNCTION_DEF_TABLE_NAME = "Function Definitions"; - static final Schema FUN_DEF_SCHEMA = FunctionDefinitionDBAdapterV1.V1_FUN_DEF_SCHEMA; + static final Schema FUN_DEF_SCHEMA = FunctionDefinitionDBAdapterV2.V2_FUN_DEF_SCHEMA; - static final int FUNCTION_DEF_NAME_COL = FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_NAME_COL; + static final int FUNCTION_DEF_NAME_COL = FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_NAME_COL; static final int FUNCTION_DEF_COMMENT_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_COMMENT_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_COMMENT_COL; static final int FUNCTION_DEF_CAT_ID_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_CAT_ID_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_CAT_ID_COL; static final int FUNCTION_DEF_RETURN_ID_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_RETURN_ID_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_RETURN_ID_COL; static final int FUNCTION_DEF_FLAGS_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_FLAGS_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_FLAGS_COL; + static final int FUNCTION_DEF_CALLCONV_COL = + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_CALLCONV_COL; static final int FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL; static final int FUNCTION_DEF_SOURCE_DT_ID_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_UNIVERSAL_DT_ID_COL; static final int FUNCTION_DEF_SOURCE_SYNC_TIME_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_SOURCE_SYNC_TIME_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_SOURCE_SYNC_TIME_COL; static final int FUNCTION_DEF_LAST_CHANGE_TIME_COL = - FunctionDefinitionDBAdapterV1.V1_FUNCTION_DEF_LAST_CHANGE_TIME_COL; + FunctionDefinitionDBAdapterV2.V2_FUNCTION_DEF_LAST_CHANGE_TIME_COL; static final byte FUNCTION_DEF_VARARG_FLAG = (byte) 0x1; // Bit 0 is flag for "has vararg". - - // Flags Bits 1..4 used for generic calling convention ID - static final int GENERIC_CALLING_CONVENTION_FLAG_MASK = 0xf; - static final int GENERIC_CALLING_CONVENTION_FLAG_SHIFT = 1; + static final byte FUNCTION_DEF_NORETURN_FLAG = (byte) 0x2; // Bit 1 is flag for "has noreturn" (added with V2). /** * Gets an adapter for working with the function definition data type database table. The adapter is based * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name + * @param callConvAdapter calling convention table adapter suitable to add new conventions + * (e.g., this adapter being used during upgrade operation). Only used when openMode is + * {@link DBConstants#UPGRADE} when adding new calling conventions must be permitted. * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of function definition data types. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task is cancelled */ static FunctionDefinitionDBAdapter getAdapter(DBHandle handle, int openMode, - TaskMonitor monitor) throws VersionException, IOException { + String tablePrefix, CallingConventionDBAdapter callConvAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + if (openMode == DBConstants.CREATE) { - return new FunctionDefinitionDBAdapterV1(handle, true); + return new FunctionDefinitionDBAdapterV2(handle, tablePrefix, true); } try { - return new FunctionDefinitionDBAdapterV1(handle, false); + return new FunctionDefinitionDBAdapterV2(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { throw e; } - FunctionDefinitionDBAdapter adapter = findReadOnlyAdapter(handle); + FunctionDefinitionDBAdapter adapter; if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = findReadOnlyAdapter(handle, tablePrefix, callConvAdapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); + } + else { + adapter = findReadOnlyAdapter(handle, tablePrefix, null); } return adapter; } @@ -88,18 +101,27 @@ abstract class FunctionDefinitionDBAdapter { /** * Tries to get a read only adapter for the database whose handle is passed to this method. * @param handle handle to prior version of the database. + * @param tablePrefix prefix to be used with default table name + * @param callConvAdapter calling convention table adapter suitable to add new conventions + * (e.g., this adapter being used during upgrade operation). Should be null if not performing + * an upgrade. * @return the read only Function Definition data type table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ - static FunctionDefinitionDBAdapter findReadOnlyAdapter(DBHandle handle) + private static FunctionDefinitionDBAdapter findReadOnlyAdapter(DBHandle handle, + String tablePrefix, CallingConventionDBAdapter callConvAdapter) throws VersionException { + try { + return new FunctionDefinitionDBAdapterV1(handle, tablePrefix, callConvAdapter); + } + catch (VersionException e) { + // ignore + } try { return new FunctionDefinitionDBAdapterV0(handle); } catch (VersionException e) { - if (!e.isUpgradable()) { - throw e; - } + // ignore } return new FunctionDefinitionDBAdapterNoTable(handle); @@ -109,29 +131,35 @@ abstract class FunctionDefinitionDBAdapter { * Upgrades the Function Definition data type table from the oldAdapter's version to the current version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor * @return the adapter for the new upgraded version of the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if the database can't be read or written. + * @throws CancelledException if task is cancelled */ - static FunctionDefinitionDBAdapter upgrade(DBHandle handle, - FunctionDefinitionDBAdapter oldAdapter) throws VersionException, IOException { + private static FunctionDefinitionDBAdapter upgrade(DBHandle handle, + FunctionDefinitionDBAdapter oldAdapter, String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); FunctionDefinitionDBAdapter tmpAdapter = null; try { - tmpAdapter = new FunctionDefinitionDBAdapterV1(tmpHandle, true); + tmpAdapter = new FunctionDefinitionDBAdapterV2(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec, false); } oldAdapter.deleteTable(handle); - FunctionDefinitionDBAdapterV1 newAdapter = - new FunctionDefinitionDBAdapterV1(handle, true); + FunctionDefinitionDBAdapter newAdapter = + new FunctionDefinitionDBAdapterV2(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec, false); } @@ -150,7 +178,8 @@ abstract class FunctionDefinitionDBAdapter { * @param categoryID the ID for the category that contains this data type. * @param returnDtID the ID of the data type that is returned by this function definition. * @param hasVarArgs true if this function definition has a variable length argument list. - * @param genericCallingConvention generic calling convention + * @param hasNoReturn true if this function definition has noreturn enabled + * @param callingConventionID calling convention ID * @param sourceArchiveID the ID for the source archive where this data type originated. * @param sourceDataTypeID the ID of the associated data type in the source archive. * @param lastChangeTime the time this data type was last changed. @@ -158,8 +187,8 @@ abstract class FunctionDefinitionDBAdapter { * @throws IOException if the database can't be accessed. */ abstract DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, - boolean hasVarArgs, GenericCallingConvention genericCallingConvention, - long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException; + boolean hasNoReturn, boolean hasVarArgs, byte callingConventionID, long sourceArchiveID, + long sourceDataTypeID, long lastChangeTime) throws IOException; /** * Gets a function signature definition data type record from the database based on its ID. @@ -174,7 +203,7 @@ abstract class FunctionDefinitionDBAdapter { * @return the function definition data type record iterator. * @throws IOException if the database can't be accessed. */ - abstract RecordIterator getRecords() throws IOException; + public abstract RecordIterator getRecords() throws IOException; /** * Removes the function definition data type record with the specified ID. @@ -227,4 +256,27 @@ abstract class FunctionDefinitionDBAdapter { abstract DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException; + /** + * Determine if the calling convention ID within record reflects a Generic Calling Convention + * ordinal (i.e., true if V1 adapter in use for read-only mode). + * See {@link #getGenericCallingConventionName(int)} for Generic Calling Convention name lookup. + * @return true if calling convention ID within record reflects a Generic Calling Convention + * ordinal. + */ + boolean usesGenericCallingConventionId() { + return false; + } + + /** + * Get old GenericCallingConvention name for specified ordinal value. + * @param ordinal old GenericCallingConvention ordinal + * @return old GenericCallingConvention name + */ + static String getGenericCallingConventionName(int ordinal) { + GenericCallingConvention genericCallingConvention = GenericCallingConvention.get(ordinal); + return genericCallingConvention != GenericCallingConvention.unknown + ? genericCallingConvention.getDeclarationName() + : CompilerSpec.CALLING_CONVENTION_unknown; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java index cd149d79c1..38721f487b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java @@ -19,7 +19,6 @@ import java.io.IOException; import db.*; import ghidra.program.database.util.EmptyRecordIterator; -import ghidra.program.model.data.GenericCallingConvention; import ghidra.util.UniversalID; /** @@ -37,15 +36,19 @@ class FunctionDefinitionDBAdapterNoTable extends FunctionDefinitionDBAdapter { } @Override - public DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, - boolean hasVarArgs, GenericCallingConvention genericCallingConvention, - long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { - throw new UnsupportedOperationException( - "Not allowed to update version prior to existence of Function Definition Data Types table."); + DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, + boolean hasNoReturn, boolean hasVarArgs, byte callingConventionID, long sourceArchiveID, + long sourceDataTypeID, long lastChangeTime) throws IOException { + throw new UnsupportedOperationException(); } @Override - public DBRecord getRecord(long functionDefID) throws IOException { + public int getRecordCount() { + return 0; + } + + @Override + DBRecord getRecord(long functionDefID) throws IOException { return null; } @@ -55,22 +58,22 @@ class FunctionDefinitionDBAdapterNoTable extends FunctionDefinitionDBAdapter { } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { throw new UnsupportedOperationException(); } @Override - public boolean removeRecord(long functionDefID) throws IOException { + boolean removeRecord(long functionDefID) throws IOException { return false; } @Override - protected void deleteTable(DBHandle handle) { + void deleteTable(DBHandle handle) { // do nothing } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return Field.EMPTY_ARRAY; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java index 5697a0069b..34ee8aec09 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java @@ -18,7 +18,8 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; -import ghidra.program.model.data.*; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeManager; import ghidra.util.UniversalID; import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.VersionException; @@ -28,12 +29,15 @@ import ghidra.util.exception.VersionException; */ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter implements RecordTranslator { + static final int VERSION = 0; + static final int V0_FUNCTION_DEF_NAME_COL = 0; static final int V0_FUNCTION_DEF_COMMENT_COL = 1; static final int V0_FUNCTION_DEF_CAT_ID_COL = 2; static final int V0_FUNCTION_DEF_RETURN_ID_COL = 3; static final int V0_FUNCTION_DEF_FLAGS_COL = 4; + // DO NOT REMOVE WHAT'S BELOW - this documents the schema used in version 0. // static final Schema V0_FUN_DEF_SCHEMA = new Schema(VERSION, "Data Type ID", // new Class[] {StringField.class, StringField.class, @@ -51,33 +55,31 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public FunctionDefinitionDBAdapterV0(DBHandle handle) throws VersionException { + FunctionDefinitionDBAdapterV0(DBHandle handle) throws VersionException { table = handle.getTable(FUNCTION_DEF_TABLE_NAME); if (table == null) { - throw new VersionException("Missing Table: " + FUNCTION_DEF_TABLE_NAME); + throw new VersionException(true); } - int version = table.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + FUNCTION_DEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } @Override - public DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, - boolean hasVarArgs, GenericCallingConvention genericCallingConvention, - long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { - throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + - " of " + FUNCTION_DEF_TABLE_NAME + " table."); + public int getRecordCount() { + return table.getRecordCount(); } @Override - public DBRecord getRecord(long functionDefID) throws IOException { + DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, + boolean hasNoReturn, boolean hasVarArgs, byte callingConventionID, long sourceArchiveID, + long sourceDataTypeID, long lastChangeTime) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + DBRecord getRecord(long functionDefID) throws IOException { return translateRecord(table.getRecord(functionDefID)); } @@ -87,22 +89,22 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { throw new UnsupportedOperationException(); } @Override - public boolean removeRecord(long functionDefID) throws IOException { - return table.deleteRecord(functionDefID); + boolean removeRecord(long functionDefID) throws IOException { + throw new UnsupportedOperationException(); } @Override - protected void deleteTable(DBHandle handle) throws IOException { + void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(FUNCTION_DEF_TABLE_NAME); } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V0_FUNCTION_DEF_CAT_ID_COL); } @@ -111,6 +113,11 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter return Field.EMPTY_ARRAY; } + @Override + DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { + return null; + } + @Override public DBRecord translateRecord(DBRecord oldRec) { if (oldRec == null) { @@ -123,6 +130,8 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter rec.setLongValue(FUNCTION_DEF_RETURN_ID_COL, oldRec.getLongValue(V0_FUNCTION_DEF_RETURN_ID_COL)); rec.setByteValue(FUNCTION_DEF_FLAGS_COL, oldRec.getByteValue(V0_FUNCTION_DEF_FLAGS_COL)); + rec.setByteValue(FUNCTION_DEF_CALLCONV_COL, + DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID); rec.setLongValue(FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL, DataTypeManager.LOCAL_ARCHIVE_KEY); rec.setLongValue(FUNCTION_DEF_SOURCE_DT_ID_COL, UniversalIdGenerator.nextID().getValue()); rec.setLongValue(FUNCTION_DEF_SOURCE_SYNC_TIME_COL, DataType.NO_SOURCE_SYNC_TIME); @@ -130,9 +139,4 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter return rec; } - @Override - DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { - return null; - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java index b3fa3de5ba..3c89118faa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java @@ -16,19 +16,24 @@ package ghidra.program.database.data; import java.io.IOException; -import java.util.Date; import db.*; -import ghidra.program.model.data.GenericCallingConvention; -import ghidra.util.Msg; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeManager; import ghidra.util.UniversalID; +import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Function Signature Definition database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. */ -class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { +class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter + implements RecordTranslator { + static final int VERSION = 1; + static final int V1_FUNCTION_DEF_NAME_COL = 0; static final int V1_FUNCTION_DEF_COMMENT_COL = 1; static final int V1_FUNCTION_DEF_CAT_ID_COL = 2; @@ -38,116 +43,91 @@ class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { static final int V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL = 6; static final int V1_FUNCTION_DEF_SOURCE_SYNC_TIME_COL = 7; static final int V1_FUNCTION_DEF_LAST_CHANGE_TIME_COL = 8; - static final Schema V1_FUN_DEF_SCHEMA = new Schema(VERSION, "Data Type ID", - new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, - LongField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, - LongField.INSTANCE, LongField.INSTANCE }, - new String[] { "Name", "Comment", "Category ID", "Return Type ID", "Flags", - "Source Archive ID", "Source Data Type ID", "Source Sync Time", "Last Change Time" }); + +// DO NOT REMOVE WHAT'S BELOW - this documents the schema used in version 0. +// static final Schema V1_FUN_DEF_SCHEMA = new Schema(VERSION, "Data Type ID", +// new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, +// LongField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, +// LongField.INSTANCE, LongField.INSTANCE }, +// new String[] { "Name", "Comment", "Category ID", "Return Type ID", "Flags", +// "Source Archive ID", "Source Data Type ID", "Source Sync Time", "Last Change Time" }); + + // Flags Bits 1..4 used for generic calling convention ID + private static final int GENERIC_CALLING_CONVENTION_FLAG_MASK = 0xf; + private static final int GENERIC_CALLING_CONVENTION_FLAG_SHIFT = 1; private Table table; + private CallingConventionDBAdapter callConvAdapter; // must be null if not performing upgrade /** - * Gets a version 1 adapter for the Function Definition database table. + * Gets a version 1 read-only adapter for the Function Definition database table. * @param handle handle to the database containing the table. - * @param create true if this constructor should create the table. + * @param tablePrefix prefix to be used with default table name + * @param callConvAdapter calling convention table adapter suitable to add new conventions + * (e.g., this adapter being used during upgrade operation). Should be null if not performing + * an upgrade in which case calling convention IDs will reflect generic convention ordinals. + * * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public FunctionDefinitionDBAdapterV1(DBHandle handle, boolean create) - throws VersionException, IOException { - - if (create) { - table = handle.createTable(FUNCTION_DEF_TABLE_NAME, V1_FUN_DEF_SCHEMA, - new int[] { V1_FUNCTION_DEF_CAT_ID_COL, V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL }); + public FunctionDefinitionDBAdapterV1(DBHandle handle, String tablePrefix, + CallingConventionDBAdapter callConvAdapter) throws VersionException { + this.callConvAdapter = callConvAdapter; + String tableName = tablePrefix + FUNCTION_DEF_TABLE_NAME; + table = handle.getTable(tableName); + if (table == null) { + throw new VersionException(true); } - else { - table = handle.getTable(FUNCTION_DEF_TABLE_NAME); - if (table == null) { - throw new VersionException("Missing Table: " + FUNCTION_DEF_TABLE_NAME); - } - int version = table.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + - FUNCTION_DEF_TABLE_NAME + " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); - } + int version = table.getSchema().getVersion(); + if (version != VERSION) { + throw new VersionException(version < VERSION); } } @Override - public DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, - boolean hasVarArgs, GenericCallingConvention genericCallingConvention, - long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { - byte flags = (byte) 0; - if (hasVarArgs) { - flags |= FunctionDefinitionDBAdapter.FUNCTION_DEF_VARARG_FLAG; - } - if (genericCallingConvention != null) { - int ordinal = genericCallingConvention.ordinal(); - if (ordinal < 0 || - ordinal > FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_MASK) { - Msg.error(this, "GenericCallingConvention ordinal unsupported: " + ordinal); - } - else { - flags |= - ordinal << FunctionDefinitionDBAdapter.GENERIC_CALLING_CONVENTION_FLAG_SHIFT; - } - } - long tableKey = table.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.FUNCTION_DEF, tableKey); - DBRecord record = V1_FUN_DEF_SCHEMA.createRecord(key); - - record.setString(V1_FUNCTION_DEF_NAME_COL, name); - record.setString(V1_FUNCTION_DEF_COMMENT_COL, comments); - record.setLongValue(V1_FUNCTION_DEF_CAT_ID_COL, categoryID); - record.setLongValue(V1_FUNCTION_DEF_RETURN_ID_COL, returnDtID); - record.setByteValue(V1_FUNCTION_DEF_FLAGS_COL, flags); - record.setLongValue(V1_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL, sourceArchiveID); - record.setLongValue(V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL, sourceDataTypeID); - record.setLongValue(V1_FUNCTION_DEF_SOURCE_SYNC_TIME_COL, lastChangeTime); - record.setLongValue(V1_FUNCTION_DEF_LAST_CHANGE_TIME_COL, lastChangeTime); - table.putRecord(record); - return record; + boolean usesGenericCallingConventionId() { + return callConvAdapter == null; } @Override - public DBRecord getRecord(long functionDefID) throws IOException { - return table.getRecord(functionDefID); + public int getRecordCount() { + return table.getRecordCount(); } @Override - public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { - if (setLastChangeTime) { - record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_LAST_CHANGE_TIME_COL, - (new Date()).getTime()); - } - table.putRecord(record); + DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, + boolean hasNoReturn, boolean hasVarArgs, byte callingConventionID, long sourceArchiveID, + long sourceDataTypeID, long lastChangeTime) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + DBRecord getRecord(long functionDefID) throws IOException { + return translateRecord(table.getRecord(functionDefID)); + } + + @Override + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + throw new UnsupportedOperationException(); } @Override public RecordIterator getRecords() throws IOException { - return table.iterator(); + return new TranslatedRecordIterator(table.iterator(), this); } @Override - public boolean removeRecord(long functionDefID) throws IOException { - return table.deleteRecord(functionDefID); + boolean removeRecord(long functionDefID) throws IOException { + throw new UnsupportedOperationException(); } @Override - protected void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(FUNCTION_DEF_TABLE_NAME); + void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(table.getName()); } @Override - public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V1_FUNCTION_DEF_CAT_ID_COL); } @@ -169,4 +149,48 @@ class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { } return null; } + + @Override + public DBRecord translateRecord(DBRecord oldRec) { + if (oldRec == null) { + return null; + } + DBRecord rec = FunctionDefinitionDBAdapter.FUN_DEF_SCHEMA.createRecord(oldRec.getKey()); + rec.setString(FUNCTION_DEF_NAME_COL, oldRec.getString(V1_FUNCTION_DEF_NAME_COL)); + rec.setString(FUNCTION_DEF_COMMENT_COL, oldRec.getString(V1_FUNCTION_DEF_COMMENT_COL)); + rec.setLongValue(FUNCTION_DEF_CAT_ID_COL, oldRec.getLongValue(V1_FUNCTION_DEF_CAT_ID_COL)); + rec.setLongValue(FUNCTION_DEF_RETURN_ID_COL, oldRec.getLongValue(V1_FUNCTION_DEF_RETURN_ID_COL)); + + byte flags = oldRec.getByteValue(V1_FUNCTION_DEF_FLAGS_COL); + int mask = GENERIC_CALLING_CONVENTION_FLAG_MASK << GENERIC_CALLING_CONVENTION_FLAG_SHIFT; + int genericCallConvId = (flags & mask) >> GENERIC_CALLING_CONVENTION_FLAG_SHIFT; + flags &= (byte) (~mask); // clear old flag bits used for calling convention + + byte callingConventionId = DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID; + try { + if (callConvAdapter == null) { + // Must use old generic calling convention ordinal as ID + callingConventionId = (byte) genericCallConvId; + } + else { + // It is expected that an open transaction exists during upgrade use + // and that all records are upgraded prior to general use + String callingConvention = getGenericCallingConventionName(genericCallConvId); + callingConventionId = callConvAdapter.getCallingConventionId(callingConvention, + cc -> { + /* ignore */ }); + } + } + catch (IOException e) { + // ignore - use unknown convention ID + } + + rec.setByteValue(FUNCTION_DEF_FLAGS_COL, flags); + rec.setByteValue(FUNCTION_DEF_CALLCONV_COL, callingConventionId); + rec.setLongValue(FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL, DataTypeManager.LOCAL_ARCHIVE_KEY); + rec.setLongValue(FUNCTION_DEF_SOURCE_DT_ID_COL, UniversalIdGenerator.nextID().getValue()); + rec.setLongValue(FUNCTION_DEF_SOURCE_SYNC_TIME_COL, DataType.NO_SOURCE_SYNC_TIME); + rec.setLongValue(FUNCTION_DEF_LAST_CHANGE_TIME_COL, DataType.NO_LAST_CHANGE_TIME); + return rec; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV2.java new file mode 100644 index 0000000000..e57118173b --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV2.java @@ -0,0 +1,164 @@ +/* ### + * 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 java.io.IOException; +import java.util.Date; + +import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; + +/** + * Version 2 implementation for accessing the Function Signature Definition database table. + */ +class FunctionDefinitionDBAdapterV2 extends FunctionDefinitionDBAdapter { + + static final int VERSION = 2; + static final int V2_FUNCTION_DEF_NAME_COL = 0; + static final int V2_FUNCTION_DEF_COMMENT_COL = 1; + static final int V2_FUNCTION_DEF_CAT_ID_COL = 2; + static final int V2_FUNCTION_DEF_RETURN_ID_COL = 3; + static final int V2_FUNCTION_DEF_FLAGS_COL = 4; + static final int V2_FUNCTION_DEF_CALLCONV_COL = 5; + static final int V2_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL = 6; + static final int V2_FUNCTION_DEF_UNIVERSAL_DT_ID_COL = 7; + static final int V2_FUNCTION_DEF_SOURCE_SYNC_TIME_COL = 8; + static final int V2_FUNCTION_DEF_LAST_CHANGE_TIME_COL = 9; + static final Schema V2_FUN_DEF_SCHEMA = new Schema(VERSION, "Data Type ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, ByteField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Name", "Comment", "Category ID", "Return Type ID", "Flags", "Call Conv ID", + "Source Archive ID", "Source Data Type ID", "Source Sync Time", "Last Change Time" }); + + private Table table; + + /** + * Gets a version 2 adapter for the Function Definition database table. + * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if an IO error occurs + */ + public FunctionDefinitionDBAdapterV2(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + FUNCTION_DEF_TABLE_NAME; + if (create) { + table = handle.createTable(tableName, V2_FUN_DEF_SCHEMA, + new int[] { V2_FUNCTION_DEF_CAT_ID_COL, V2_FUNCTION_DEF_UNIVERSAL_DT_ID_COL }); + } + else { + table = handle.getTable(tableName); + if (table == null) { + throw new VersionException(true); + } + int version = table.getSchema().getVersion(); + if (version != VERSION) { + throw new VersionException(version < VERSION); + } + } + } + + @Override + public int getRecordCount() { + return table.getRecordCount(); + } + + @Override + DBRecord createRecord(String name, String comments, long categoryID, long returnDtID, + boolean hasNoReturn, boolean hasVarArgs, byte callingConventionID, long sourceArchiveID, + long sourceDataTypeID, long lastChangeTime) throws IOException { + byte flags = (byte) 0; + if (hasVarArgs) { + flags |= FUNCTION_DEF_VARARG_FLAG; + } + if (hasNoReturn) { + flags |= FUNCTION_DEF_NORETURN_FLAG; + } + long tableKey = table.getKey(); + long key = DataTypeManagerDB.createKey(DataTypeManagerDB.FUNCTION_DEF, tableKey); + DBRecord record = V2_FUN_DEF_SCHEMA.createRecord(key); + + record.setString(V2_FUNCTION_DEF_NAME_COL, name); + record.setString(V2_FUNCTION_DEF_COMMENT_COL, comments); + record.setLongValue(V2_FUNCTION_DEF_CAT_ID_COL, categoryID); + record.setLongValue(V2_FUNCTION_DEF_RETURN_ID_COL, returnDtID); + record.setByteValue(V2_FUNCTION_DEF_FLAGS_COL, flags); + record.setByteValue(V2_FUNCTION_DEF_CALLCONV_COL, callingConventionID); + record.setLongValue(V2_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL, sourceArchiveID); + record.setLongValue(V2_FUNCTION_DEF_UNIVERSAL_DT_ID_COL, sourceDataTypeID); + record.setLongValue(V2_FUNCTION_DEF_SOURCE_SYNC_TIME_COL, lastChangeTime); + record.setLongValue(V2_FUNCTION_DEF_LAST_CHANGE_TIME_COL, lastChangeTime); + table.putRecord(record); + return record; + } + + @Override + DBRecord getRecord(long functionDefID) throws IOException { + return table.getRecord(functionDefID); + } + + @Override + void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + if (setLastChangeTime) { + record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_LAST_CHANGE_TIME_COL, + (new Date()).getTime()); + } + table.putRecord(record); + } + + @Override + public RecordIterator getRecords() throws IOException { + return table.iterator(); + } + + @Override + boolean removeRecord(long functionDefID) throws IOException { + return table.deleteRecord(functionDefID); + } + + @Override + void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(table.getName()); + } + + @Override + Field[] getRecordIdsInCategory(long categoryID) throws IOException { + return table.findRecords(new LongField(categoryID), V2_FUNCTION_DEF_CAT_ID_COL); + } + + @Override + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return table.findRecords(new LongField(archiveID), V2_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL); + } + + @Override + DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { + Field[] keys = table.findRecords(new LongField(datatypeID.getValue()), + V2_FUNCTION_DEF_UNIVERSAL_DT_ID_COL); + + for (int i = 0; i < keys.length; i++) { + DBRecord record = table.getRecord(keys[i]); + if (record.getLongValue(V2_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { + return record; + } + } + return null; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java index ef141fc156..6dc3b77bdd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -39,22 +40,24 @@ abstract class FunctionParameterAdapter { FunctionParameterAdapterV1.V1_PARAMETER_DT_LENGTH_COL; /** - * Gets an adapter for working with the function definition parameters database table. The adapter is based - * on the version of the database associated with the specified database handle and the openMode. + * Gets an adapter for working with the function definition parameters database table. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of function definition parameters. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task is cancelled */ - static FunctionParameterAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + static FunctionParameterAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { if (openMode == DBConstants.CREATE) { - return new FunctionParameterAdapterV1(handle, true); + return new FunctionParameterAdapterV1(handle, tablePrefix, true); } try { - return new FunctionParameterAdapterV1(handle, false); + return new FunctionParameterAdapterV1(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -62,7 +65,7 @@ abstract class FunctionParameterAdapter { } FunctionParameterAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } @@ -74,7 +77,8 @@ abstract class FunctionParameterAdapter { * @return the read only Function Definition Parameters table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ - static FunctionParameterAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { + private static FunctionParameterAdapter findReadOnlyAdapter(DBHandle handle) + throws VersionException { try { return new FunctionParameterAdapterV0(handle); } @@ -91,28 +95,35 @@ abstract class FunctionParameterAdapter { * current version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor * @return the adapter for the new upgraded version of the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if the database can't be read or written. + * @throws CancelledException if task is cancelled */ - static FunctionParameterAdapter upgrade(DBHandle handle, FunctionParameterAdapter oldAdapter) - throws VersionException, IOException { + private static FunctionParameterAdapter upgrade(DBHandle handle, + FunctionParameterAdapter oldAdapter, String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); FunctionParameterAdapter tmpAdapter = null; try { - tmpAdapter = new FunctionParameterAdapterV1(tmpHandle, true); + tmpAdapter = new FunctionParameterAdapterV1(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec); } oldAdapter.deleteTable(handle); - FunctionParameterAdapterV1 newAdapter = new FunctionParameterAdapterV1(handle, true); + FunctionParameterAdapterV1 newAdapter = + new FunctionParameterAdapterV1(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java index b23f18271c..fb0b4dfaee 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java @@ -26,6 +26,7 @@ import ghidra.util.exception.VersionException; class FunctionParameterAdapterV0 extends FunctionParameterAdapter implements RecordTranslator { static final int VERSION = 0; + // Parameter Table Columns static final int V0_PARAMETER_PARENT_ID_COL = 0; static final int V0_PARAMETER_DT_ID_COL = 1; @@ -52,34 +53,15 @@ class FunctionParameterAdapterV0 extends FunctionParameterAdapter implements Rec if (parameterTable == null) { throw new VersionException(true); } - int version = parameterTable.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + PARAMETER_TABLE_NAME + - " but got " + parameterTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + if (parameterTable.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } @Override public DBRecord createRecord(long dataTypeID, long parentID, int ordinal, String name, String comment, int dtLength) throws IOException { - - long tableKey = parameterTable.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.PARAMETER, tableKey); - DBRecord record = V0_PARAMETER_SCHEMA.createRecord(key); - record.setLongValue(V0_PARAMETER_PARENT_ID_COL, parentID); - record.setLongValue(V0_PARAMETER_DT_ID_COL, dataTypeID); - record.setString(V0_PARAMETER_NAME_COL, name); - record.setString(V0_PARAMETER_COMMENT_COL, comment); - record.setIntValue(V0_PARAMETER_ORDINAL_COL, ordinal); - parameterTable.putRecord(record); - return record; + throw new UnsupportedOperationException(); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java index f67a7ea810..3a063a4550 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java @@ -22,6 +22,8 @@ import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Function Definition Parameters database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. */ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { static final int VERSION = 1; @@ -45,30 +47,27 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { /** * Gets a version 1 adapter for the Function Definition Parameter database table. * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. + * @throws IOException if an IO error occurs */ - public FunctionParameterAdapterV1(DBHandle handle, boolean create) + public FunctionParameterAdapterV1(DBHandle handle, String tablePrefix, boolean create) throws VersionException, IOException { - + String tableName = tablePrefix + PARAMETER_TABLE_NAME; if (create) { - table = handle.createTable(PARAMETER_TABLE_NAME, V1_PARAMETER_SCHEMA, + table = handle.createTable(tableName, V1_PARAMETER_SCHEMA, new int[] { V1_PARAMETER_PARENT_ID_COL }); } else { - table = handle.getTable(PARAMETER_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { throw new VersionException(true); } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + PARAMETER_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } } @@ -76,12 +75,7 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { @Override public DBRecord createRecord(long dataTypeID, long parentID, int ordinal, String name, String comment, int dtLength) throws IOException { - - long tableKey = table.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.PARAMETER, tableKey); + long key = DataTypeManagerDB.createKey(DataTypeManagerDB.PARAMETER, table.getKey()); DBRecord record = V1_PARAMETER_SCHEMA.createRecord(key); record.setLongValue(V1_PARAMETER_PARENT_ID_COL, parentID); record.setLongValue(V1_PARAMETER_DT_ID_COL, dataTypeID); @@ -115,7 +109,7 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { @Override protected void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(PARAMETER_TABLE_NAME); + handle.deleteTable(table.getName()); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java index 6b2b8daabb..4a7b69a1a4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java @@ -21,7 +21,6 @@ import java.util.Set; import db.DBConstants; import db.DBHandle; import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; /** * @@ -31,16 +30,25 @@ import ghidra.util.task.TaskMonitor; * */ abstract class ParentChildAdapter { - static final String TABLE_NAME = "DT_PARENT_CHILD"; + static final String PARENT_CHILD_TABLE_NAME = "DT_PARENT_CHILD"; - static ParentChildAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) + /** + * Gets an adapter for working with the function definition parameters database table. + * @param handle handle to the database to be accessed. + * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name + * @return the adapter for accessing the table of function definition parameters. + * @throws VersionException if the database handle's version doesn't match the expected version. + * @throws IOException if there is trouble accessing the database. + */ + static ParentChildAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix) throws VersionException, IOException { if (openMode == DBConstants.CREATE) { - return new ParentChildDBAdapterV0(handle, true); + return new ParentChildDBAdapterV0(handle, tablePrefix, true); } try { - return new ParentChildDBAdapterV0(handle, false); + return new ParentChildDBAdapterV0(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -48,20 +56,20 @@ abstract class ParentChildAdapter { } ParentChildAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix); } return adapter; } } - static ParentChildAdapter findReadOnlyAdapter(DBHandle handle) { + private static ParentChildAdapter findReadOnlyAdapter(DBHandle handle) { return new ParentChildDBAdapterNoTable(handle); } - static ParentChildAdapter upgrade(DBHandle handle, ParentChildAdapter oldAdapter) - throws VersionException, IOException { + private static ParentChildAdapter upgrade(DBHandle handle, ParentChildAdapter oldAdapter, + String tablePrefix) throws VersionException, IOException { - ParentChildDBAdapterV0 adapter = new ParentChildDBAdapterV0(handle, true); + ParentChildDBAdapterV0 adapter = new ParentChildDBAdapterV0(handle, tablePrefix, true); adapter.setNeedsInitializing(); return adapter; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java index d2278d3f40..e9f4a22984 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java @@ -22,6 +22,11 @@ import java.util.Set; import db.*; import ghidra.util.exception.VersionException; +/** + * Version 0 implementation for accessing the datatype parent/child database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. + */ class ParentChildDBAdapterV0 extends ParentChildAdapter { private static final int VERSION = 0; @@ -35,19 +40,28 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter { private Table table; private boolean needsInitializing = false; - ParentChildDBAdapterV0(DBHandle handle, boolean create) throws VersionException, IOException { - + /** + * Gets a version 1 adapter for the datatype parent/child database table. + * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if an IO error occurs + */ + ParentChildDBAdapterV0(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + PARENT_CHILD_TABLE_NAME; if (create) { - table = handle.createTable(TABLE_NAME, V0_SCHEMA, new int[] { PARENT_COL, CHILD_COL }); + table = handle.createTable(tableName, V0_SCHEMA, new int[] { PARENT_COL, CHILD_COL }); } else { - table = handle.getTable(TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { throw new VersionException(true); } - if (table.getSchema().getVersion() != 0) { - throw new VersionException("Expected version 0 for table " + TABLE_NAME + - " but got " + table.getSchema().getVersion()); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java index 3d45173f83..95e3f9bfdc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -36,14 +37,26 @@ abstract class PointerDBAdapter implements RecordTranslator { static final int PTR_CATEGORY_COL = 1; static final int PTR_LENGTH_COL = 2; - static PointerDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + /** + * Gets an adapter for working with the enumeration data type values database table. The adapter is based + * on the version of the database associated with the specified database handle and the openMode. + * @param handle handle to the database to be accessed. + * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name + * @param monitor the monitor to use for displaying status or for canceling. + * @return the adapter for accessing the table of enumeration data type values. + * @throws VersionException if the database handle's version doesn't match the expected version. + * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task is cancelled + */ + static PointerDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { if (openMode == DBConstants.CREATE) { - return new PointerDBAdapterV2(handle, true); + return new PointerDBAdapterV2(handle, tablePrefix, true); } try { - return new PointerDBAdapterV2(handle, false); + return new PointerDBAdapterV2(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -51,7 +64,7 @@ abstract class PointerDBAdapter implements RecordTranslator { } PointerDBAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter, monitor); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } @@ -67,22 +80,25 @@ abstract class PointerDBAdapter implements RecordTranslator { } static PointerDBAdapter upgrade(DBHandle handle, PointerDBAdapter oldAdapter, - TaskMonitor monitor) throws VersionException, IOException { + String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); PointerDBAdapter tmpAdapter = null; try { - tmpAdapter = new PointerDBAdapterV2(tmpHandle, true); + tmpAdapter = new PointerDBAdapterV2(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec); } oldAdapter.deleteTable(handle); - PointerDBAdapter newAdapter = new PointerDBAdapterV2(handle, true); + PointerDBAdapter newAdapter = new PointerDBAdapterV2(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java index 3890b5e22a..ec5a2a82b4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java @@ -20,36 +20,46 @@ import java.io.IOException; import db.*; import ghidra.util.exception.VersionException; +/** + * Version 2 implementation for accessing the PointerDB database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. + */ class PointerDBAdapterV2 extends PointerDBAdapter { final static int VERSION = 2; private Table table; - PointerDBAdapterV2(DBHandle handle, boolean create) throws VersionException, IOException { - + /** + * Gets a version 2 adapter for the PointerDB database table. + * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if IO error occurs + */ + PointerDBAdapterV2(DBHandle handle, String tablePrefix, boolean create) + throws VersionException, IOException { + String tableName = tablePrefix + POINTER_TABLE_NAME; if (create) { - table = handle.createTable(POINTER_TABLE_NAME, SCHEMA, new int[] { PTR_CATEGORY_COL }); + table = handle.createTable(tableName, SCHEMA, new int[] { PTR_CATEGORY_COL }); } else { - table = handle.getTable(POINTER_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { - throw new VersionException("Missing Table: " + POINTER_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } - else if (table.getSchema().getVersion() != VERSION) { - int version = table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(true); - } - throw new VersionException(VersionException.NEWER_VERSION, false); + int version = table.getSchema().getVersion(); + if (version != VERSION) { + throw new VersionException(version < VERSION); } } } @Override DBRecord createRecord(long dataTypeID, long categoryID, int length) throws IOException { - long tableKey = table.getKey(); - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.POINTER, tableKey); - + long key = DataTypeManagerDB.createKey(DataTypeManagerDB.POINTER, table.getKey()); DBRecord record = SCHEMA.createRecord(key); record.setLongValue(PTR_DT_ID_COL, dataTypeID); record.setLongValue(PTR_CATEGORY_COL, categoryID); @@ -85,7 +95,7 @@ class PointerDBAdapterV2 extends PointerDBAdapter { @Override void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(POINTER_TABLE_NAME); + handle.deleteTable(table.getName()); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java index 20cde8ca57..d004105cd5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java @@ -50,6 +50,9 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB * @param handle open database handle * @param addrMap the address map (instance settings not supported if null) * @param openMode the program open mode (see {@link DBConstants}) + * @param tablePrefix DB table prefix to be applied to all associated table names. This + * need only be specified when using multiple instances with the same + * DB handle (null or empty string for no-prefix). * @param errHandler the database io error handler * @param lock the program synchronization lock * @param monitor the progress monitor @@ -57,18 +60,18 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB * @throws VersionException if the database does not match the expected version. * @throws IOException if a database IO error occurs. */ - public ProgramBasedDataTypeManagerDB(DBHandle handle, AddressMap addrMap, int openMode, - ErrorHandler errHandler, Lock lock, TaskMonitor monitor) + protected ProgramBasedDataTypeManagerDB(DBHandle handle, AddressMap addrMap, int openMode, + String tablePrefix, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) throws CancelledException, VersionException, IOException { - super(handle, addrMap, openMode, errHandler, lock, monitor); + super(handle, addrMap, openMode, tablePrefix, errHandler, lock, monitor); } protected void initializeOtherAdapters(int openMode, TaskMonitor monitor) throws CancelledException, IOException, VersionException { if (addrMap != null) { instanceSettingsAdapter = - SettingsDBAdapter.getAdapter(INSTANCE_SETTINGS_TABLE_NAME, dbHandle, openMode, - addrMap, monitor); + SettingsDBAdapter.getAdapter(tablePrefix + INSTANCE_SETTINGS_TABLE_NAME, dbHandle, + openMode, addrMap, monitor); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java index 2db4e3c414..2f73ce80fd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java @@ -31,8 +31,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; import ghidra.program.util.ChangeManager; import ghidra.util.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; /** @@ -61,7 +60,7 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB public ProgramDataTypeManager(DBHandle handle, AddressMap addrMap, int openMode, ErrorHandler errHandler, Lock lock, TaskMonitor monitor) throws CancelledException, VersionException, IOException { - super(handle, addrMap, openMode, errHandler, lock, monitor); + super(handle, addrMap, openMode, null, errHandler, lock, monitor); upgrade = (openMode == DBConstants.UPGRADE); } @@ -79,8 +78,16 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB @Override public void setProgram(ProgramDB p) { this.program = p; - dataOrganization = p.getCompilerSpec().getDataOrganization(); - removeOldFileNameList(); + try { + setProgramArchitecture(p, p.getSymbolTable().getVariableStorageManager(), false, + TaskMonitor.DUMMY); + } + catch (CancelledException e) { + throw new AssertException(e); // unexpected - no IO performed + } + catch (IOException e) { + errHandler.dbError(e); + } if (upgrade) { removeOldFileNameList(); } @@ -102,11 +109,22 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB public void programReady(int openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { if (openMode == DBConstants.UPGRADE) { - doSourceArchiveUpdates(program.getCompilerSpec(), monitor); + doSourceArchiveUpdates(monitor); migrateOldFlexArrayComponentsIfRequired(monitor); } } + /** + * Update program-architecture information following a language upgrade/change + * @param monitor task monitor + * @throws IOException if IO error occurs + * @throws CancelledException if task monitor cancelled + */ + public void languageChanged(TaskMonitor monitor) throws IOException, CancelledException { + setProgramArchitecture(program, program.getSymbolTable().getVariableStorageManager(), true, + monitor); + } + @Override public String getName() { return program.getName(); @@ -296,12 +314,4 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB return ArchiveType.PROGRAM; } - @Override - public DataOrganization getDataOrganization() { - if (dataOrganization == null) { - dataOrganization = program.getCompilerSpec().getDataOrganization(); - } - return dataOrganization; - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java index debb9cae8c..645ada0f5f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapter.java @@ -22,6 +22,7 @@ import java.util.List; import db.*; import ghidra.program.model.data.SourceArchive; import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -32,8 +33,10 @@ import ghidra.util.task.TaskMonitor; */ abstract class SourceArchiveAdapter { - static final String TABLE_NAME = "Data Type Archive IDs"; + static final String SOURCE_ARCHIVE_TABLE_NAME = "Data Type Archive IDs"; + static final Schema SCHEMA = SourceArchiveAdapterV0.V0_SCHEMA; + // Data Type Archive ID Columns static final int ARCHIVE_ID_DOMAIN_FILE_ID_COL = SourceArchiveAdapterV0.V0_ARCHIVE_ID_DOMAIN_FILE_ID_COL; @@ -52,18 +55,20 @@ abstract class SourceArchiveAdapter { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of data type archive ID entries. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task is cancelled */ - static SourceArchiveAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + static SourceArchiveAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { if (openMode == DBConstants.CREATE) { - return new SourceArchiveAdapterV0(handle, true); + return new SourceArchiveAdapterV0(handle, tablePrefix, true); } try { - return new SourceArchiveAdapterV0(handle, false); + return new SourceArchiveAdapterV0(handle, tablePrefix, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -71,7 +76,7 @@ abstract class SourceArchiveAdapter { } SourceArchiveAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } @@ -83,7 +88,8 @@ abstract class SourceArchiveAdapter { * @return the read only Data Type Archive ID table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ - static SourceArchiveAdapter findReadOnlyAdapter(DBHandle handle) { + private static SourceArchiveAdapter findReadOnlyAdapter(DBHandle handle) + throws VersionException { return new SourceArchiveAdapterNoTable(handle); } @@ -95,24 +101,28 @@ abstract class SourceArchiveAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if the database can't be read or written. + * @throws CancelledException if task is cancelled */ - static SourceArchiveAdapter upgrade(DBHandle handle, SourceArchiveAdapter oldAdapter) - throws VersionException, IOException { + private static SourceArchiveAdapter upgrade(DBHandle handle, SourceArchiveAdapter oldAdapter, + String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); SourceArchiveAdapter tmpAdapter = null; try { - tmpAdapter = new SourceArchiveAdapterV0(tmpHandle, true); + tmpAdapter = new SourceArchiveAdapterV0(tmpHandle, tablePrefix, true); Iterator it = oldAdapter.getRecords().iterator(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec); } oldAdapter.deleteTable(handle); - SourceArchiveAdapter newAdapter = new SourceArchiveAdapterV0(handle, true); + SourceArchiveAdapter newAdapter = new SourceArchiveAdapterV0(handle, tablePrefix, true); it = tmpAdapter.getRecords().iterator(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec); } @@ -125,22 +135,33 @@ abstract class SourceArchiveAdapter { } } + /** + * Delete table from database + * @param handle database handle + * @throws IOException if IO error occurs + */ abstract void deleteTable(DBHandle handle) throws IOException; /** * Creates a new source archive record using the information from the given source archive. * @param sourceArchive the source archive from which to get the archive information. + * @return new archive record which corresponds to specified sourceArchive + * @throws IOException if IO error occurs */ abstract DBRecord createRecord(SourceArchive sourceArchive) throws IOException; /** * Returns a list containing all records in the archive table - * @return + * @return list of all archive records + * @throws IOException if IO error occurs */ abstract List getRecords() throws IOException; /** * Returns the record for the given key (sourceArchiveID) + * @param key ID of data type archive record + * @return archive record or null if not found + * @throws IOException if IO error occurs */ abstract DBRecord getRecord(long key) throws IOException; @@ -153,7 +174,7 @@ abstract class SourceArchiveAdapter { /** * Remove the record for the given data type archive ID. - * @param dataTypeArchiveID ID of data type archive record to delete + * @param key ID of data type archive record to delete * @return true if the record was deleted * @throws IOException if there was a problem accessing the database */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java index f1dd2e7951..906fb60ad3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java @@ -26,6 +26,8 @@ import ghidra.util.exception.VersionException; /** * Version 0 implementation for accessing the Data Type Archive ID database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. */ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { static final int VERSION = 0; @@ -45,38 +47,33 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { /** * Gets a version 1 adapter for the Data Type Archive ID table. * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. + * @throws IOException if an IO errr occurs */ - public SourceArchiveAdapterV0(DBHandle handle, boolean create) + public SourceArchiveAdapterV0(DBHandle handle, String tablePrefix, boolean create) throws VersionException, IOException { - + String tableName = tablePrefix + SOURCE_ARCHIVE_TABLE_NAME; if (create) { - table = handle.createTable(TABLE_NAME, V0_SCHEMA); - + table = handle.createTable(tableName, V0_SCHEMA); createRecordForLocalManager(); } else { - table = handle.getTable(TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { throw new VersionException(true); } - int version = table.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } } /** - * - * @throws IOException + * Create standard entry which corresponds to local datatype manager + * @throws IOException if an IO error occurs */ private void createRecordForLocalManager() throws IOException { DBRecord record = V0_SCHEMA.createRecord(DataTypeManager.LOCAL_ARCHIVE_KEY); @@ -129,6 +126,7 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { @Override protected void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(table.getName()); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java index 3b59b71c03..745a69d886 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java @@ -19,7 +19,6 @@ import java.util.HashMap; import java.util.Map; import ghidra.program.model.data.*; -import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.CompilerSpecID; import ghidra.util.UniversalID; @@ -31,8 +30,8 @@ public class SourceArchiveUpgradeMap { new long[] { OLD_CLIB_ARCHIVE_ID, OLD_NTDDK_ARCHIVE_ID, OLD_WINDOWS_ARCHIVE_ID }; private CompilerSpecID WINDOWS_CSPEC_ID = new CompilerSpecID("windows"); - private Map windowsMap; - private Map defaultMap; + + private Map oldArchiveRemappings; public SourceArchiveUpgradeMap() { @@ -47,34 +46,25 @@ public class SourceArchiveUpgradeMap { SourceArchive newDefaultClibArchive = new SourceArchiveImpl(NEW_DEFAULT_CLIB_ARCHIVE_ID, NEW_DEFAULT_CLIB_ARCHIVE_NAME); - // create mapping for WINDOWS - windowsMap = new HashMap(); - windowsMap.put(new UniversalID(OLD_CLIB_ARCHIVE_ID), newWindowsArchive); - windowsMap.put(new UniversalID(OLD_WINDOWS_ARCHIVE_ID), newWindowsArchive); - windowsMap.put(new UniversalID(OLD_NTDDK_ARCHIVE_ID), newWindowsArchive); + oldArchiveRemappings = new HashMap(); - // create defaultMap - defaultMap = new HashMap(); - defaultMap.put(new UniversalID(OLD_CLIB_ARCHIVE_ID), newDefaultClibArchive); + // create mappings for old Windows archives + oldArchiveRemappings.put(new UniversalID(OLD_CLIB_ARCHIVE_ID), newWindowsArchive); + oldArchiveRemappings.put(new UniversalID(OLD_WINDOWS_ARCHIVE_ID), newWindowsArchive); + oldArchiveRemappings.put(new UniversalID(OLD_NTDDK_ARCHIVE_ID), newWindowsArchive); + + // create mappings for old default archives + oldArchiveRemappings.put(new UniversalID(OLD_CLIB_ARCHIVE_ID), newDefaultClibArchive); + + // create mappings for old removed archives SourceArchive removedSourceArchive = new SourceArchiveImpl(); - defaultMap.put(new UniversalID(OLD_WINDOWS_ARCHIVE_ID), removedSourceArchive); - defaultMap.put(new UniversalID(OLD_NTDDK_ARCHIVE_ID), removedSourceArchive); + oldArchiveRemappings.put(new UniversalID(OLD_WINDOWS_ARCHIVE_ID), removedSourceArchive); + oldArchiveRemappings.put(new UniversalID(OLD_NTDDK_ARCHIVE_ID), removedSourceArchive); } - public SourceArchive getMappedSourceArchive(SourceArchive sourceArchive, - CompilerSpec compiler) { - if (compiler != null) { - CompilerSpecID compilerSpecID = compiler.getCompilerSpecID(); - if (WINDOWS_CSPEC_ID.equals(compilerSpecID)) { - SourceArchive replacementSourceArchive = - windowsMap.get(sourceArchive.getSourceArchiveID()); - if (replacementSourceArchive != null) { - return replacementSourceArchive; - } - } - } - return defaultMap.get(sourceArchive.getSourceArchiveID()); + public SourceArchive getMappedSourceArchive(SourceArchive sourceArchive) { + return oldArchiveRemappings.get(sourceArchive.getSourceArchiveID()); } public static boolean isReplacedSourceArchive(long id) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index 6fbbece0c1..19987ba5ac 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -376,7 +376,7 @@ class StructureDB extends CompositeDB implements StructureInternal { componentName, comment); } - // handle aligned bitfield insertion + // handle aligned bitfield insertion (packing enabled) BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0); return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment); } @@ -1860,7 +1860,8 @@ class StructureDB extends CompositeDB implements StructureInternal { @Override protected void fixupComponents() throws IOException { boolean isPacked = isPackingEnabled(); - boolean didChange = false; + boolean forceRepack = false; + boolean changed = false; boolean warn = false; int n = components.size(); for (int i = 0; i < n; i++) { @@ -1870,7 +1871,8 @@ class StructureDB extends CompositeDB implements StructureInternal { continue; // length can't change } if (dt instanceof BitFieldDataType) { - // TODO: could get messy + // Always repack if bitfields present and packing enabled + forceRepack |= isPacked; continue; } int length = DataTypeComponent.usesZeroLengthComponent(dt) ? 0 : dt.getLength(); @@ -1881,18 +1883,18 @@ class StructureDB extends CompositeDB implements StructureInternal { if (dtcLen != length) { if (isPacked) { dtc.setLength(length, true); - didChange = true; + changed = true; } else if (length < dtcLen) { dtc.setLength(length, true); shiftOffsets(i + 1, dtcLen - length, 0); // updates structure record and last modified time - didChange = true; + changed = true; } else if (length > dtcLen) { int consumed = consumeBytesAfter(i, length - dtcLen); // updates component record if (consumed > 0) { shiftOffsets(i + 1, -consumed, 0); // updates structure record and last modified time - didChange = true; + changed = true; } } if (dtc.getLength() != length) { @@ -1900,11 +1902,14 @@ class StructureDB extends CompositeDB implements StructureInternal { } } } - if (didChange) { + if (changed || forceRepack) { // Do not notify parents - must be invoked in composite dependency order - repack(false, false); - compositeAdapter.updateRecord(record, true); - dataMgr.dataTypeChanged(this, false); + // Treat as an auto-change as a result of data organization change + changed |= repack(true, false); + } + if (changed) { + compositeAdapter.updateRecord(record, false); // force record update + dataMgr.dataTypeChanged(this, true); } if (warn) { Msg.warn(this, "Failed to resize one or more structure components: " + getPathName()); @@ -2238,10 +2243,8 @@ class StructureDB extends CompositeDB implements StructureInternal { checkAncestry(replacementDt); } catch (Exception e) { - // TODO: should we use Undefined1 instead to avoid cases where DEFAULT datatype can - // not be used (bitfield, aligned structure, etc.) - // TODO: failing silently is rather hidden - replacementDt = DataType.DEFAULT; + // TODO: should we flag bad replacement + replacementDt = isPackingEnabled() ? Undefined1DataType.dataType : DataType.DEFAULT; } boolean changed = false; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java index a2746aa88a..f8a07fd032 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java @@ -19,6 +19,7 @@ import java.io.IOException; import db.*; import ghidra.util.UniversalID; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -51,15 +52,18 @@ abstract class TypedefDBAdapter { * on the version of the database associated with the specified database handle and the openMode. * @param handle handle to the database to be accessed. * @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE). + * @param tablePrefix prefix to be used with default table name * @param monitor the monitor to use for displaying status or for canceling. * @return the adapter for accessing the table of Typedef data types. * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. + * @throws CancelledException if task is cancelled */ - static TypedefDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + static TypedefDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix, + TaskMonitor monitor) + throws VersionException, IOException, CancelledException { try { - return new TypedefDBAdapterV2(handle, openMode == DBConstants.CREATE); + return new TypedefDBAdapterV2(handle, tablePrefix, openMode == DBConstants.CREATE); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -67,7 +71,7 @@ abstract class TypedefDBAdapter { } TypedefDBAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); + adapter = upgrade(handle, adapter, tablePrefix, monitor); } return adapter; } @@ -79,7 +83,7 @@ abstract class TypedefDBAdapter { * @return the read only Typedef table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ - static TypedefDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { + private static TypedefDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { try { return new TypedefDBAdapterV1(handle); } @@ -93,28 +97,34 @@ abstract class TypedefDBAdapter { * Upgrades the Typedef data type table from the oldAdapter's version to the current version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. + * @param tablePrefix prefix to be used with default table name + * @param monitor task monitor * @return the adapter for the new upgraded version of the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if the database can't be read or written. + * @throws CancelledException if task is cancelled */ - static TypedefDBAdapter upgrade(DBHandle handle, TypedefDBAdapter oldAdapter) - throws VersionException, IOException { + private static TypedefDBAdapter upgrade(DBHandle handle, TypedefDBAdapter oldAdapter, + String tablePrefix, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { DBHandle tmpHandle = new DBHandle(); long id = tmpHandle.startTransaction(); TypedefDBAdapter tmpAdapter = null; try { - tmpAdapter = new TypedefDBAdapterV2(tmpHandle, true); + tmpAdapter = new TypedefDBAdapterV2(tmpHandle, tablePrefix, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); tmpAdapter.updateRecord(rec, false); } oldAdapter.deleteTable(handle); - TypedefDBAdapter newAdapter = new TypedefDBAdapterV2(handle, true); + TypedefDBAdapter newAdapter = new TypedefDBAdapterV2(handle, tablePrefix, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { + monitor.checkCanceled(); DBRecord rec = it.next(); newAdapter.updateRecord(rec, false); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java index 6eaa2a2d3c..3bbac383f5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java @@ -28,15 +28,19 @@ import ghidra.util.exception.VersionException; * Version 0 implementation for accessing the Typedef database table. */ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { + static final int VERSION = 0; + private static final int V0_TYPEDEF_DT_ID_COL = 0; private static final int V0_TYPEDEF_NAME_COL = 1; private static final int V0_TYPEDEF_CAT_COL = 2; + // DO NOT REMOVE WHAT'S BELOW - this documents the schema used in version 0. // static final Schema V0_SCHEMA = new Schema(VERSION, "Typedef ID", // new Class[] {LongField.class, StringField.class, // LongField.class}, // new String[] {"Data Type ID", "Name", "Category ID"}); + private Table table; /** @@ -51,14 +55,8 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { if (table == null) { throw new VersionException("Missing Table: " + TYPEDEF_TABLE_NAME); } - int version = table.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(false); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java index d89c386594..dde15e7e51 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java @@ -25,7 +25,9 @@ import ghidra.util.exception.VersionException; * Version 1 implementation for accessing the Typedef database table. */ class TypedefDBAdapterV1 extends TypedefDBAdapter implements RecordTranslator { + static final int VERSION = 1; + static final int V1_TYPEDEF_DT_ID_COL = 0; static final int V1_TYPEDEF_NAME_COL = 1; static final int V1_TYPEDEF_CAT_COL = 2; @@ -40,6 +42,7 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter implements RecordTranslator { // LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, // new String[] { "Data Type ID", "Name", "Category ID", "Source Archive ID", // "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); + private Table table; /** @@ -56,12 +59,7 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter implements RecordTranslator { } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java index 39226fa23d..d67dd66eb5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java @@ -24,9 +24,13 @@ import ghidra.util.exception.VersionException; /** * Version 2 implementation for accessing the Typedef database table. + * + * NOTE: Use of tablePrefix introduced with this adapter version. */ class TypedefDBAdapterV2 extends TypedefDBAdapter { + static final int VERSION = 2; + static final int V2_TYPEDEF_DT_ID_COL = 0; static final int V2_TYPEDEF_FLAGS_COL = 1; static final int V2_TYPEDEF_NAME_COL = 2; @@ -35,61 +39,53 @@ class TypedefDBAdapterV2 extends TypedefDBAdapter { static final int V2_TYPEDEF_UNIVERSAL_DT_ID_COL = 5; static final int V2_TYPEDEF_SOURCE_SYNC_TIME_COL = 6; static final int V2_TYPEDEF_LAST_CHANGE_TIME_COL = 7; + static final Schema V2_SCHEMA = new Schema(VERSION, "Typedef ID", new Field[] { LongField.INSTANCE, ShortField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, new String[] { "Data Type ID", "Flags", "Name", "Category ID", "Source Archive ID", "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); + private Table table; /** * Gets a version 1 adapter for the Typedef database table. * @param handle handle to the database containing the table. + * @param tablePrefix prefix to be used with default table name * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. * @throws IOException if IO error occurs */ - public TypedefDBAdapterV2(DBHandle handle, boolean create) + public TypedefDBAdapterV2(DBHandle handle, String tablePrefix, boolean create) throws VersionException, IOException { - + String tableName = tablePrefix + TYPEDEF_TABLE_NAME; if (create) { - table = handle.createTable(TYPEDEF_TABLE_NAME, V2_SCHEMA, + table = handle.createTable(tableName, V2_SCHEMA, new int[] { V2_TYPEDEF_CAT_COL, V2_TYPEDEF_UNIVERSAL_DT_ID_COL }); } else { - table = handle.getTable(TYPEDEF_TABLE_NAME); + table = handle.getTable(tableName); if (table == null) { - throw new VersionException("Missing Table: " + TYPEDEF_TABLE_NAME); + throw new VersionException("Missing Table: " + tableName); } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + throw new VersionException(version < VERSION); } } } @Override void deleteTable(DBHandle handle) throws IOException { - handle.deleteTable(TYPEDEF_TABLE_NAME); + handle.deleteTable(table.getName()); } @Override public DBRecord createRecord(long dataTypeID, String name, short flags, long categoryID, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { - - long tableKey = table.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.TYPEDEF, tableKey); - + long key = DataTypeManagerDB.createKey(DataTypeManagerDB.TYPEDEF, table.getKey()); DBRecord record = V2_SCHEMA.createRecord(key); record.setLongValue(V2_TYPEDEF_DT_ID_COL, dataTypeID); record.setShortValue(V2_TYPEDEF_FLAGS_COL, flags); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java index dd8869a237..3072a52070 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java @@ -497,12 +497,10 @@ class UnionDB extends CompositeDB implements UnionInternal { } } if (changed) { - // NOTE: since we do not retain our external alignment we have no way of knowing if - // it has changed, so we must assume it has if we are an aligned union - // Do not notify parents - if (!repack(false, false)) { - dataMgr.dataTypeChanged(this, false); - } + // Do not notify parents - must be invoked in composite dependency order + // Treat as an auto-change as a result of data organization change + repack(true, false); + dataMgr.dataTypeChanged(this, true); } } @@ -562,8 +560,8 @@ class UnionDB extends CompositeDB implements UnionInternal { DataType baseDataType = bitfieldDt.getBaseDataType(); baseDataType = resolve(baseDataType); - // Both aligned and non-packed bitfields use same adjustment - // non-packed must force bitfield placement at byte offset 0 + // Both packed and non-packed bitfields use same adjustment + // Non-packed must force bitfield placement at byte offset 0 int bitSize = bitfieldDt.getDeclaredBitSize(); int effectiveBitSize = BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength()); @@ -795,9 +793,8 @@ class UnionDB extends CompositeDB implements UnionInternal { checkAncestry(replacementDt); } catch (Exception e) { - // TODO: should we use Undefined instead since we do not support - // DEFAULT in Unions - replacementDt = DataType.DEFAULT; + // TODO: should we flag bad replacement + replacementDt = Undefined1DataType.dataType; } boolean changed = false; for (int i = components.size() - 1; i >= 0; i--) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapter.java deleted file mode 100644 index 0c7bcc1e45..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* ### - * 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.function; - -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - -import java.io.IOException; - -import db.*; - -/** - * Adapter to access the Function Calling Conventions tables. - */ -abstract class CallingConventionDBAdapter { - static final byte UNKNOWN_CALLING_CONVENTION_ID = (byte) 0; - static final byte DEFAULT_CALLING_CONVENTION_ID = (byte) 1; - - static final Schema CALLING_CONVENTION_SCHEMA = - CallingConventionDBAdapterV0.V0_CALLING_CONVENTION_SCHEMA; - // Calling Convention Columns - static final int CALLING_CONVENTION_NAME_COL = - CallingConventionDBAdapterV0.V0_CALLING_CONVENTION_NAME_COL; - - static CallingConventionDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { - if (openMode == DBConstants.CREATE) { - return new CallingConventionDBAdapterV0(handle, true); - } - try { - return new CallingConventionDBAdapterV0(handle, false); - } - catch (VersionException e) { - if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { - throw e; - } - CallingConventionDBAdapter adapter = findReadOnlyAdapter(handle); - if (openMode == DBConstants.UPGRADE) { - adapter = upgrade(handle, adapter); - } - return adapter; - } - } - - static CallingConventionDBAdapter findReadOnlyAdapter(DBHandle handle) throws IOException { - try { - return new CallingConventionDBAdapterV0(handle, false); - } - catch (VersionException e) { - } - - return new CallingConventionDBAdapterNoTable(); - } - - static CallingConventionDBAdapter upgrade(DBHandle handle, CallingConventionDBAdapter oldAdapter) - throws VersionException, IOException { - return new CallingConventionDBAdapterV0(handle, true); - } - - abstract DBRecord createCallingConventionRecord(String name) throws IOException; - - abstract DBRecord getCallingConventionRecord(byte callingConventionID) throws IOException; - - abstract DBRecord getCallingConventionRecord(String name) throws IOException; - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterNoTable.java deleted file mode 100644 index ac7033ef0e..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterNoTable.java +++ /dev/null @@ -1,59 +0,0 @@ -/* ### - * 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.function; - -import java.io.IOException; - -import db.DBRecord; - -/** - * Adapter needed for a read-only version of Program that is not going - * to be upgraded, and there is no Calling Convention table in the Program. - * - */ -class CallingConventionDBAdapterNoTable extends CallingConventionDBAdapter { - - /** - * - */ - public CallingConventionDBAdapterNoTable() { - } - - /* (non-Javadoc) - * @see ghidra.program.database.function.CallingConventionDBAdapter#createCallingConventionRecord(java.lang.String) - */ - @Override - public DBRecord createCallingConventionRecord(String name) throws IOException { - return null; - } - - /* (non-Javadoc) - * @see ghidra.program.database.function.CallingConventionDBAdapter#getCallingConventionRecord(byte) - */ - @Override - public DBRecord getCallingConventionRecord(byte callingConventionID) throws IOException { - return null; - } - - /* (non-Javadoc) - * @see ghidra.program.database.function.CallingConventionDBAdapter#getCallingConventionRecord(java.lang.String) - */ - @Override - public DBRecord getCallingConventionRecord(String name) throws IOException { - return null; - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java deleted file mode 100644 index 8e9f9e2942..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java +++ /dev/null @@ -1,109 +0,0 @@ -/* ### - * 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.function; - -import java.io.IOException; - -import db.*; -import ghidra.util.exception.VersionException; - -/** - * Version 0 implementation for the calling conventions tables adapter. - * - */ -class CallingConventionDBAdapterV0 extends CallingConventionDBAdapter { - - static final String CALLING_CONVENTION_TABLE_NAME = "Calling Conventions"; - - // Calling Convention Columns - // Key field is the Calling convention ID, which is a Byte field. - static final int V0_CALLING_CONVENTION_NAME_COL = 0; - - static final Schema V0_CALLING_CONVENTION_SCHEMA = new Schema(0, ByteField.INSTANCE, "ID", - new Field[] { StringField.INSTANCE }, new String[] { "Name" }); - - private Table callingConventionTable; - - /** - * Constructor - * - */ - public CallingConventionDBAdapterV0(DBHandle handle, boolean create) - throws VersionException, IOException { - - if (create) { - // No additional indexed fields. - callingConventionTable = handle.createTable(CALLING_CONVENTION_TABLE_NAME, - V0_CALLING_CONVENTION_SCHEMA, new int[] {}); - } - else { - callingConventionTable = handle.getTable(CALLING_CONVENTION_TABLE_NAME); - if (callingConventionTable == null) { - throw new VersionException(true); - } - if (callingConventionTable.getSchema().getVersion() != 0) { - throw new VersionException(VersionException.NEWER_VERSION, false); - } - } - } - - @Override - public DBRecord createCallingConventionRecord(String name) throws IOException { - byte key = getFirstAvailableKey(); - DBRecord record = V0_CALLING_CONVENTION_SCHEMA.createRecord(new ByteField(key)); - record.setString(V0_CALLING_CONVENTION_NAME_COL, name); - callingConventionTable.putRecord(record); - return record; - } - - /** - * Get the first unused key value. Remember 0 is reserved for unknown and 1 for default. - * @return the first available key. This is a number for 2 to 255. - * @throws IOException if there are no more available keys. - */ - private byte getFirstAvailableKey() throws IOException { - byte key = 2; - for (; key < 256; key++) { - DBRecord record = getCallingConventionRecord(key); - if (record == null) { - return key; - } - } - if (key >= 256) { - throw new IOException("No more keys available for calling conventions."); - } - return key; - } - - @Override - public DBRecord getCallingConventionRecord(byte callingConventionID) throws IOException { - return callingConventionTable.getRecord(new ByteField(callingConventionID)); - } - - @Override - public DBRecord getCallingConventionRecord(String name) throws IOException { - RecordIterator iterator = callingConventionTable.iterator(); - while (iterator.hasNext()) { - DBRecord record = iterator.next(); - String callingConventionName = record.getString(V0_CALLING_CONVENTION_NAME_COL); - if (callingConventionName.equals(name)) { - return record; - } - } - return null; - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapterV3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapterV3.java index 0d64b65a70..f45946f00b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapterV3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapterV3.java @@ -15,15 +15,15 @@ */ package ghidra.program.database.function; +import java.io.IOException; + +import db.*; +import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.map.AddressMap; import ghidra.program.model.listing.Function; import ghidra.program.model.symbol.SourceType; import ghidra.util.exception.VersionException; -import java.io.IOException; - -import db.*; - class FunctionAdapterV3 extends FunctionAdapter { // @@ -116,7 +116,7 @@ class FunctionAdapterV3 extends FunctionAdapter { rec.setByteValue(FUNCTION_FLAGS_COL, getSignatureSourceFlagBits(SourceType.DEFAULT)); rec.setLongValue(RETURN_DATA_TYPE_ID_COL, returnDataTypeId); rec.setByteValue(CALLING_CONVENTION_ID_COL, - CallingConventionDBAdapter.UNKNOWN_CALLING_CONVENTION_ID); + DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID); rec.setIntValue(STACK_PURGE_COL, Function.UNKNOWN_STACK_DEPTH_CHANGE); table.putRecord(rec); return rec; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java index fa60100546..5cf78369dc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java @@ -229,11 +229,6 @@ public class FunctionDB extends DatabaseObject implements Function { return super.hashCode(); } - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ @Override public String toString() { return getName(true); @@ -826,7 +821,7 @@ public class FunctionDB extends DatabaseObject implements Function { PrototypeModel callingConvention = getCallingConvention(); if (callingConvention == null) { - callingConvention = getDefaultCallingConvention(); + callingConvention = manager.getDefaultCallingConvention(); } if (callingConvention == null) { return; @@ -1310,6 +1305,7 @@ public class FunctionDB extends DatabaseObject implements Function { source); } + /** * Increment updateInProgressCount indicating that an update operation is in progress and * that any attempted refresh should be deferred. The updateRefreshReqd flag will be set @@ -2517,11 +2513,6 @@ public class FunctionDB extends DatabaseObject implements Function { } } - /* - * (non-Javadoc) - * - * @see ghidra.program.model.listing.Function#getCallingConvention() - */ @Override public PrototypeModel getCallingConvention() { String name = getCallingConventionName(); @@ -2535,11 +2526,6 @@ public class FunctionDB extends DatabaseObject implements Function { return functionMgr.getCallingConvention(name); } - /* - * (non-Javadoc) - * - * @see ghidra.program.model.listing.Function#getCallingConventionName() - */ @Override public String getCallingConventionName() { manager.lock.acquire(); @@ -2551,14 +2537,13 @@ public class FunctionDB extends DatabaseObject implements Function { return thunkedFunction.getCallingConventionName(); } byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL); - if (callingConventionID == CallingConventionDBAdapter.UNKNOWN_CALLING_CONVENTION_ID) { + if (callingConventionID == DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID) { return Function.UNKNOWN_CALLING_CONVENTION_STRING; } - if (callingConventionID == CallingConventionDBAdapter.DEFAULT_CALLING_CONVENTION_ID) { + if (callingConventionID == DataTypeManagerDB.DEFAULT_CALLING_CONVENTION_ID) { return Function.DEFAULT_CALLING_CONVENTION_STRING; } - String name = manager.getCallingConventionName(callingConventionID); - return name != null ? name : UNKNOWN_CALLING_CONVENTION_STRING; + return program.getDataTypeManager().getCallingConventionName(callingConventionID); } finally { manager.lock.release(); @@ -2569,36 +2554,16 @@ public class FunctionDB extends DatabaseObject implements Function { if (thunkedFunction != null) { return thunkedFunction.getRealCallingConventionName(); } - String name = null; byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL); - if (callingConventionID != CallingConventionDBAdapter.UNKNOWN_CALLING_CONVENTION_ID && - callingConventionID != CallingConventionDBAdapter.DEFAULT_CALLING_CONVENTION_ID) { - name = manager.getCallingConventionName(callingConventionID); + String name = program.getDataTypeManager().getCallingConventionName(callingConventionID); + if (UNKNOWN_CALLING_CONVENTION_STRING.equals(name) || + DEFAULT_CALLING_CONVENTION_STRING.equals(name)) { + name = null; } // null returned for unknown or default calling convention return name; } - private PrototypeModel getDefaultCallingConvention() { - CompilerSpec compilerSpec = getProgram().getCompilerSpec(); - if (compilerSpec != null) { - return compilerSpec.getDefaultCallingConvention(); - } - return null; - } - - @Override - public String getDefaultCallingConventionName() { - PrototypeModel defaultPrototype = getDefaultCallingConvention(); - if (defaultPrototype != null) { - String defaultPrototypeName = defaultPrototype.getName(); - if (defaultPrototypeName != null) { - return defaultPrototypeName; - } - } - return Function.DEFAULT_CALLING_CONVENTION_STRING; - } - @Override public void setCallingConvention(String name) throws InvalidInputException { manager.lock.acquire(); @@ -2610,36 +2575,38 @@ public class FunctionDB extends DatabaseObject implements Function { return; } - byte newCallingConventionID = manager.getCallingConventionID(name); + byte newCallingConventionID = + program.getDataTypeManager().getCallingConventionID(name, true); byte oldCallingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL); - if (oldCallingConventionID != newCallingConventionID) { + if (oldCallingConventionID == newCallingConventionID) { + return; // no change + } + loadVariables(); + + rec.setByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL, newCallingConventionID); + manager.getFunctionAdapter().updateFunctionRecord(rec); + + boolean hasCustomStorage = hasCustomVariableStorage(); + if (!hasCustomStorage) { + // remove 'this' param if switching to __thiscall with dynamic storage + removeExplicitThisParameter(); + } + + frame.setInvalid(); + + if (!hasCustomStorage) { + createClassStructIfNeeded(); // TODO: How should thunks within Class namespace be handled? loadVariables(); - - rec.setByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL, newCallingConventionID); - manager.getFunctionAdapter().updateFunctionRecord(rec); - - boolean hasCustomStorage = hasCustomVariableStorage(); - if (!hasCustomStorage) { - // remove 'this' param if switching to __thiscall with dynamic storage - removeExplicitThisParameter(); - } - - frame.setInvalid(); - - if (!hasCustomStorage) { - createClassStructIfNeeded(); // TODO: How should thunks within Class namespace be handled? - loadVariables(); - removeExplicitThisParameter(); - updateParametersAndReturn(); // assign dynamic storage - manager.functionChanged(this, ChangeManager.FUNCTION_CHANGED_PARAMETERS); - manager.functionChanged(this, ChangeManager.FUNCTION_CHANGED_RETURN); - } - else { - manager.functionChanged(this, 0); // change did not affect parameters - } + removeExplicitThisParameter(); + updateParametersAndReturn(); // assign dynamic storage + manager.functionChanged(this, ChangeManager.FUNCTION_CHANGED_PARAMETERS); + manager.functionChanged(this, ChangeManager.FUNCTION_CHANGED_RETURN); + } + else { + manager.functionChanged(this, 0); // change did not affect parameters } } catch (IOException e) { @@ -2654,7 +2621,7 @@ public class FunctionDB extends DatabaseObject implements Function { void createClassStructIfNeeded() { PrototypeModel callingConvention = getCallingConvention(); if (callingConvention == null || - callingConvention.getGenericCallingConvention() != GenericCallingConvention.thiscall) { + !CompilerSpec.CALLING_CONVENTION_thiscall.equals(callingConvention.getName())) { return; } Namespace parent = getParentNamespace(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java index 24053a6151..72eb40c7a4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java @@ -26,6 +26,7 @@ import generic.FilteredIterator; import ghidra.program.database.DBObjectCache; import ghidra.program.database.ProgramDB; import ghidra.program.database.code.CodeManager; +import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.external.ExternalLocationDB; import ghidra.program.database.map.AddressMap; import ghidra.program.database.symbol.*; @@ -60,12 +61,10 @@ public class FunctionManagerDB implements FunctionManager { private DBObjectCache cache; private FunctionAdapter adapter; private ThunkFunctionAdapter thunkAdapter; - private CallingConventionDBAdapter callingConventionAdapter; - private Map callingConventionNameToIDMap = new HashMap<>(); - private Map callingConventionIDToNameMap = new HashMap<>(); private NamespaceManager namespaceMgr; private SymbolManager symbolMgr; private CodeManager codeMgr; + private DataTypeManagerDB dtMgr; private FunctionTagManagerDB functionTagManager; private Namespace globalNamespace; @@ -125,8 +124,6 @@ public class FunctionManagerDB implements FunctionManager { } adapter = FunctionAdapter.getAdapter(dbHandle, openMode, addrMap, monitor); thunkAdapter = ThunkFunctionAdapter.getAdapter(dbHandle, openMode, addrMap, monitor); - callingConventionAdapter = - CallingConventionDBAdapter.getAdapter(dbHandle, openMode, monitor); } @Override @@ -138,136 +135,21 @@ public class FunctionManagerDB implements FunctionManager { return adapter; } - /** - * Get calling convention name corresponding to existing ID. If id is no longer valid, - * null will be returned. - * @param id - * @return - */ - String getCallingConventionName(byte id) { - if (id == CallingConventionDBAdapter.DEFAULT_CALLING_CONVENTION_ID) { - return Function.DEFAULT_CALLING_CONVENTION_STRING; - } - else if (id == CallingConventionDBAdapter.UNKNOWN_CALLING_CONVENTION_ID) { - return null; - } - String name = callingConventionIDToNameMap.get(id); - if (name != null) { - return name; - } - try { - DBRecord record = callingConventionAdapter.getCallingConventionRecord(id); - if (record == null) { - return null; - } - - name = record.getString(CallingConventionDBAdapter.CALLING_CONVENTION_NAME_COL); - CompilerSpec compilerSpec = program.getCompilerSpec(); - PrototypeModel callingConvention = compilerSpec.getCallingConvention(name); - if (callingConvention != null) { - callingConventionIDToNameMap.put(id, name); - callingConventionNameToIDMap.put(name, id); - return name; - } - } - catch (IOException e) { - dbError(e); - } - return null; - } - - /** - * Get (and assign if needed) the ID associated with the specified calling convention name. - * @param name calling convention name - * @return calling convention ID - * @throws IOException - * @throws InvalidInputException - */ - byte getCallingConventionID(String name) throws InvalidInputException, IOException { - if (name == null || name.equals(Function.UNKNOWN_CALLING_CONVENTION_STRING)) { - return CallingConventionDBAdapter.UNKNOWN_CALLING_CONVENTION_ID; - } - else if (name.equals(Function.DEFAULT_CALLING_CONVENTION_STRING)) { - return CallingConventionDBAdapter.DEFAULT_CALLING_CONVENTION_ID; - } - Byte id = callingConventionNameToIDMap.get(name); - if (id != null) { - return id; - } - CompilerSpec compilerSpec = program.getCompilerSpec(); - PrototypeModel callingConvention = compilerSpec.getCallingConvention(name); - if (callingConvention == null) { - throw new InvalidInputException("Invalid calling convention name: " + name); - } - DBRecord record = callingConventionAdapter.getCallingConventionRecord(name); - if (record == null) { - record = callingConventionAdapter.createCallingConventionRecord(name); - } - byte newId = record.getKeyField().getByteValue(); - callingConventionIDToNameMap.put(newId, name); - callingConventionNameToIDMap.put(name, newId); - return newId; - } - @Override - public List getCallingConventionNames() { - CompilerSpec compilerSpec = program.getCompilerSpec(); - PrototypeModel[] namedCallingConventions = compilerSpec.getCallingConventions(); - List names = new ArrayList<>(namedCallingConventions.length + 2); - names.add(Function.UNKNOWN_CALLING_CONVENTION_STRING); - names.add(Function.DEFAULT_CALLING_CONVENTION_STRING); - for (PrototypeModel model : namedCallingConventions) { - names.add(model.getName()); - } - return names; + public Collection getCallingConventionNames() { + return dtMgr.getDefinedCallingConventionNames(); } @Override public PrototypeModel getDefaultCallingConvention() { CompilerSpec compilerSpec = program.getCompilerSpec(); - if (compilerSpec == null) { - return null; - } return compilerSpec.getDefaultCallingConvention(); } @Override public PrototypeModel getCallingConvention(String name) { CompilerSpec compilerSpec = program.getCompilerSpec(); - if (compilerSpec == null) { - return null; - } - if (Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(name)) { - return null; - } - if (Function.DEFAULT_CALLING_CONVENTION_STRING.equals(name)) { - return getDefaultCallingConvention(); - } - PrototypeModel[] models = compilerSpec.getCallingConventions(); - for (PrototypeModel model : models) { - String modelName = model.getName(); - if (modelName != null && modelName.equals(name)) { - return model; - } - } - return null; - } - - @Override - public PrototypeModel[] getCallingConventions() { - CompilerSpec compilerSpec = program.getCompilerSpec(); - if (compilerSpec == null) { - return new PrototypeModel[0]; - } - ArrayList namedList = new ArrayList<>(); - PrototypeModel[] models = compilerSpec.getCallingConventions(); - for (PrototypeModel model : models) { - String name = model.getName(); - if (name != null && name.length() > 0) { - namedList.add(model); - } - } - return namedList.toArray(new PrototypeModel[namedList.size()]); + return compilerSpec.getCallingConvention(name); } /** @@ -751,7 +633,8 @@ public class FunctionManagerDB implements FunctionManager { this.program = program; namespaceMgr = program.getNamespaceManager(); codeMgr = program.getCodeManager(); - symbolMgr = (SymbolManager) program.getSymbolTable(); + dtMgr = program.getDataTypeManager(); + symbolMgr = program.getSymbolTable(); globalNamespace = program.getGlobalNamespace(); functionTagManager.setProgram(program); } @@ -935,8 +818,6 @@ public class FunctionManagerDB implements FunctionManager { callFixupMap = null; lastFuncID = -1; cache.invalidate(); - callingConventionIDToNameMap.clear(); - callingConventionNameToIDMap.clear(); } finally { lock.release(); @@ -1037,9 +918,8 @@ public class FunctionManagerDB implements FunctionManager { } /** - * Construct a function iterator over all functions residing in memory starting from the specified - * entry point address. - * @param start starting address for iteration + * Construct a function iterator over all functions residing in specified address set. + * @param addrSet address set over which to iterate * @param forward if true iterate forward from start, otherwise iterate in reverse */ FunctionIteratorDB(AddressSetView addrSet, boolean forward) { @@ -1096,9 +976,6 @@ public class FunctionManagerDB implements FunctionManager { return list.iterator(); } - /** - * Set the new body for the function. - */ void setFunctionBody(FunctionDB function, AddressSetView newBody) throws OverlappingFunctionException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java index 5fa6301a53..61aa942f18 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java @@ -24,6 +24,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import db.*; +import db.util.ErrorHandler; import ghidra.program.database.*; import ghidra.program.database.code.CodeManager; import ghidra.program.database.external.ExternalManagerDB; @@ -63,6 +64,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { private LabelHistoryAdapter historyAdapter; private DBObjectCache cache; + private ErrorHandler errHandler; private ProgramDB program; private ReferenceDBManager refManager; private NamespaceManager namespaceMgr; @@ -81,22 +83,26 @@ public class SymbolManager implements SymbolTable, ManagerDB { * @param handle the database handler * @param addrMap the address map. * @param openMode the open mode. + * @param errHandler database error handler * @param lock the program synchronization lock * @param monitor the progress monitor used when upgrading. * @throws CancelledException if the user cancels the upgrade. * @throws IOException if a database io error occurs. * @throws VersionException if the database version doesn't match the current version. */ - public SymbolManager(DBHandle handle, AddressMap addrMap, int openMode, Lock lock, - TaskMonitor monitor) throws CancelledException, IOException, VersionException { + public SymbolManager(DBHandle handle, AddressMap addrMap, int openMode, ErrorHandler errHandler, + Lock lock, TaskMonitor monitor) + throws CancelledException, IOException, VersionException { this.addrMap = addrMap; + this.errHandler = errHandler; this.lock = lock; dynamicSymbolAddressMap = new AddressMapImpl((byte) 0x40, addrMap.getAddressFactory()); initializeAdapters(handle, openMode, monitor); cache = new DBObjectCache<>(100); - variableStorageMgr = new VariableStorageManagerDB(handle, addrMap, openMode, lock, monitor); + variableStorageMgr = + new VariableStorageManagerDB(handle, addrMap, openMode, errHandler, lock, monitor); if (openMode == DBConstants.UPGRADE && OldVariableStorageManagerDB.isOldVariableStorageManagerUpgradeRequired(handle)) { @@ -140,7 +146,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { this.program = program; refManager = program.getReferenceManager(); namespaceMgr = program.getNamespaceManager(); - variableStorageMgr.setProgram(program); + variableStorageMgr.setProgramArchitecture(program); } @Override @@ -177,6 +183,14 @@ public class SymbolManager implements SymbolTable, ManagerDB { } } + /** + * Get the variable storage manager used by this symbol table + * @return varable storage manager + */ + public VariableStorageManager getVariableStorageManager() { + return variableStorageMgr; + } + /** * Check for and upgrade old namespace symbol addresses which included a namespace ID. *

@@ -1579,7 +1593,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { } void dbError(IOException e) { - program.dbError(e); + errHandler.dbError(e); } void validateSource(String name, Address address, SymbolType symbolType, SourceType source) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java index 273eaa1943..f6a179343b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java @@ -39,6 +39,7 @@ abstract class VariableStorageDBAdapter { * @param openMode the openmode * @param addrMap the address map * @param monitor the progress monitor. + * @return variable storage table adapter * @throws VersionException if the database table does not match the adapter. * @throws CancelledException if the user cancels an upgrade. * @throws IOException if a database io error occurs. @@ -95,4 +96,6 @@ abstract class VariableStorageDBAdapter { abstract int getRecordCount(); + abstract void deleteTable() throws IOException; + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java index 3440fdbd6f..451f8f1876 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java @@ -60,4 +60,9 @@ public class VariableStorageDBAdapterNoTable extends VariableStorageDBAdapter { int getRecordCount() { return 0; } + + @Override + void deleteTable() { + // do nothing + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java index 0314bdac08..0980897408 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java @@ -26,10 +26,11 @@ public class VariableStorageDBAdapterV2 extends VariableStorageDBAdapter { private static final int TABLE_VERSION = 2; private Table variableStorageTable; + private DBHandle handle; VariableStorageDBAdapterV2(DBHandle handle, boolean create) throws VersionException, IOException { - + this.handle = handle; if (create) { variableStorageTable = handle.createTable(VARIABLE_STORAGE_TABLE_NAME, VARIABLE_STORAGE_SCHEMA, new int[] { HASH_COL }); @@ -49,6 +50,11 @@ public class VariableStorageDBAdapterV2 extends VariableStorageDBAdapter { } } + @Override + void deleteTable() throws IOException { + handle.deleteTable(VARIABLE_STORAGE_TABLE_NAME); + } + @Override long getNextStorageID() { long nextKey = variableStorageTable.getMaxKey() + 1; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManager.java new file mode 100644 index 0000000000..bad8835c3f --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManager.java @@ -0,0 +1,34 @@ +/* ### + * 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.symbol; + +import java.io.IOException; + +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.VariableStorage; + +public interface VariableStorageManager { + + /** + * Get a variable address for the given storage specification. + * @param storage variable storage specification + * @param create if true a new variable address will be allocated if needed + * @return variable address which corresponds to the storage specification or null if not found + * and create is false. + * @throws IOException if an IO error occurs + */ + Address getVariableStorageAddress(VariableStorage storage, boolean create) throws IOException; +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManagerDB.java index e0a07e9d76..f116150b68 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageManagerDB.java @@ -19,10 +19,13 @@ import java.io.IOException; import java.util.List; import db.*; -import ghidra.program.database.*; +import db.util.ErrorHandler; +import ghidra.program.database.DBObjectCache; +import ghidra.program.database.DatabaseObject; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.ProgramArchitecture; import ghidra.program.model.listing.VariableStorage; import ghidra.program.model.pcode.Varnode; import ghidra.program.util.LanguageTranslator; @@ -32,11 +35,11 @@ import ghidra.util.datastruct.WeakValueHashMap; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -public class VariableStorageManagerDB { +public class VariableStorageManagerDB implements VariableStorageManager { - private ProgramDB program; - private AddressMap addrMap; + private ProgramArchitecture arch; private Lock lock; + private ErrorHandler errorHandler; private VariableStorageDBAdapter adapter; @@ -47,45 +50,55 @@ public class VariableStorageManagerDB { /** * Construct a new variable manager. * @param handle the database handle. - * @param addrMap the address map + * @param addrMap the address map (required for legacy adpter use only) * @param openMode the open mode + * @param errorHandler database error handler * @param lock the program synchronization lock * @param monitor the task monitor. * @throws IOException if a database error occurs. * @throws VersionException if the table version is different from this adapter. - * @throws IOException + * @throws IOException if an IO error occurs * @throws CancelledException if the user cancels the upgrade. */ - public VariableStorageManagerDB(DBHandle handle, AddressMap addrMap, int openMode, Lock lock, - TaskMonitor monitor) throws VersionException, IOException, CancelledException { - - this.addrMap = addrMap; + public VariableStorageManagerDB(DBHandle handle, AddressMap addrMap, int openMode, + ErrorHandler errorHandler, Lock lock, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + this.errorHandler = errorHandler; this.lock = lock; - adapter = VariableStorageDBAdapter.getAdapter(handle, openMode, addrMap, monitor); } + /** + * Set program architecture. + * @param arch program architecture + */ + public void setProgramArchitecture(ProgramArchitecture arch) { + this.arch = arch; + } + + /** + * Delete the DB table which correspnds to this variable storage implementation + * @param dbHandle database handle + * @throws IOException if an IO error occurs + */ + public static void delete(DBHandle dbHandle) throws IOException { + dbHandle.deleteTable(VariableStorageDBAdapter.VARIABLE_STORAGE_TABLE_NAME); + } + + /** + * Determine if the variable storage manager table already exists + * @param dbHandle database handle + * @return true if storage table exists + */ + public static boolean exists(DBHandle dbHandle) { + return dbHandle.getTable(VariableStorageDBAdapter.VARIABLE_STORAGE_TABLE_NAME) != null; + } + void invalidateCache(boolean all) { cache.invalidate(); cacheMap.clear(); } - void setProgram(ProgramDB program) { - this.program = program; - } - -// private void cacheNamespaceStorage(long namespaceID) throws IOException { -// variableAddrLookupCache.clear(); -// storageLookupCache.clear(); -// lastNamespaceCacheID = namespaceID; -// Record[] records = adapter.getRecordsForNamespace(namespaceID); -// for (Record rec : records) { -// MyVariableStorage varStore = new MyVariableStorage(rec); -// variableAddrLookupCache.put(varStore.getVariableAddress(), varStore); -// storageLookupCache.put(varStore.getVariableStorage(), varStore); -// } -// } - private MyVariableStorage getMyVariableStorage(Address variableAddr) throws IOException { if (!variableAddr.isVariableAddress()) { throw new IllegalArgumentException("Address is not a VariableAddress: " + variableAddr); @@ -103,6 +116,14 @@ public class VariableStorageManagerDB { return varStore; } + /** + * Get the list of varnodes associated with the specified variable storage address. + * NOTE: The program architecture and error handler must be set appropriately prior to + * invocation of this method (see {@link #setProgramArchitecture(ProgramArchitecture, ErrorHandler)}. + * @param variableAddr variable storage address + * @return storage varnode list or null if address unknown + * @throws IOException if a database IO error occurs + */ List getStorageVarnodes(Address variableAddr) throws IOException { if (!variableAddr.isVariableAddress()) { throw new IllegalArgumentException(); @@ -112,7 +133,7 @@ public class VariableStorageManagerDB { return null; } try { - return VariableStorage.getVarnodes(program.getAddressFactory(), + return VariableStorage.getVarnodes(arch.getAddressFactory(), rec.getString(VariableStorageDBAdapter.STORAGE_COL)); } catch (InvalidInputException e) { @@ -121,6 +142,14 @@ public class VariableStorageManagerDB { return null; } + /** + * Get the variable storage object associated with the specified variable storage address. + * NOTE: The program architecture and error handler must be set appropriately prior to + * invocation of this method (see {@link #setProgramArchitecture(ProgramArchitecture, ErrorHandler)}. + * @param variableAddr variable storage address + * @return variable storage object or null if address unknown + * @throws IOException if a database IO error occurs + */ VariableStorage getVariableStorage(Address variableAddr) throws IOException { MyVariableStorage myStorage = getMyVariableStorage(variableAddr); if (myStorage != null) { @@ -129,6 +158,17 @@ public class VariableStorageManagerDB { return null; } + /** + * Get a variable address for the given storage specification. + * NOTE: The program architecture and error handler must be set appropriately prior to + * invocation of this method (see {@link #setProgramArchitecture(ProgramArchitecture, ErrorHandler)}. + * @param storage variable storage specification + * @param create if true a new variable address will be allocated if needed + * @return variable address which corresponds to the storage specification or null if not found + * and create is false. + * @throws IOException if an IO error occurs + */ + @Override public Address getVariableStorageAddress(VariableStorage storage, boolean create) throws IOException { long hash = storage.getLongHash(); @@ -215,7 +255,7 @@ public class VariableStorageManagerDB { super(cache, record.getKey()); this.record = record; try { - storage = VariableStorage.deserialize(program, + storage = VariableStorage.deserialize(arch, record.getString(VariableStorageDBAdapter.STORAGE_COL)); } catch (InvalidInputException e) { @@ -250,7 +290,7 @@ public class VariableStorageManagerDB { } record = rec; try { - storage = VariableStorage.deserialize(program, + storage = VariableStorage.deserialize(arch, record.getString(VariableStorageDBAdapter.STORAGE_COL)); } catch (InvalidInputException e) { @@ -260,7 +300,7 @@ public class VariableStorageManagerDB { } } catch (IOException e) { - program.dbError(e); + errorHandler.dbError(e); } finally { lock.release(); @@ -272,10 +312,13 @@ public class VariableStorageManagerDB { /** * Perform language translation. + * Following the invocation of this method it is important to ensure that the program + * architecure is adjusted if neccessary. * Update variable storage specifications to reflect address space and register mappings - * @param translator - * @param monitor - * @throws CancelledException + * @param translator language translator to be used for mapping storage varnodes to new + * architecture. + * @param monitor task monitor + * @throws CancelledException if task is cancelled */ public void setLanguage(LanguageTranslator translator, TaskMonitor monitor) throws CancelledException { @@ -308,7 +351,7 @@ public class VariableStorageManagerDB { } } catch (IOException e) { - program.dbError(e); + errorHandler.dbError(e); } finally { invalidateCache(true); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DBRecordAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DBRecordAdapter.java index 8b939d0536..d07edefa77 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DBRecordAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DBRecordAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +15,6 @@ */ package ghidra.program.database.util; -import ghidra.program.model.address.Address; - import java.io.IOException; import db.RecordIterator; @@ -29,11 +26,15 @@ import db.RecordIterator; public interface DBRecordAdapter { /** - * Get a record iterator. - * @param start start of iterator - * @param end end of iterator - * @param colIndex index column + * Get a record iterator for all records. + * @return record iterator * @throws IOException if there was a problem accessing the database */ - public RecordIterator getRecords(Address start, Address end, int colIndex) throws IOException; + public RecordIterator getRecords() throws IOException; + + /** + * Get the number of records in table + * @return total record count + */ + public int getRecordCount(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java index 4d09c023f1..2aa7b34763 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java @@ -36,7 +36,6 @@ public abstract class AbstractDataType implements DataType { protected String name; protected CategoryPath categoryPath; protected final DataTypeManager dataMgr; - private DataOrganization dataOrganization; protected AbstractDataType(CategoryPath path, String name, DataTypeManager dataTypeManager) { if (path == null) { @@ -75,16 +74,8 @@ public abstract class AbstractDataType implements DataType { @Override public final DataOrganization getDataOrganization() { - if (dataOrganization != null) { - return dataOrganization; - } - if (dataMgr != null) { - dataOrganization = dataMgr.getDataOrganization(); - } - if (dataOrganization == null) { - dataOrganization = DataOrganizationImpl.getDefaultOrganization(); - } - return dataOrganization; + return dataMgr != null ? dataMgr.getDataOrganization() + : DataOrganizationImpl.getDefaultOrganization(); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java index 0e5d906ed6..3e17923920 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractIntegerDataType.java @@ -56,18 +56,14 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt private static SettingsDefinition[] SETTINGS_DEFS = { FormatSettingsDefinition.DEF_HEX, PADDING, ENDIAN, MNEMONIC }; - private final boolean signed; - /** * Constructor * * @param name a unique signed/unsigned data-type name (also used as the mnemonic) - * @param signed true if signed, false if unsigned * @param dtm data-type manager whose data organization should be used */ - public AbstractIntegerDataType(String name, boolean signed, DataTypeManager dtm) { + public AbstractIntegerDataType(String name, DataTypeManager dtm) { super(null, name, dtm); - this.signed = signed; } /** @@ -86,11 +82,10 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt } /** + * Determine if this type is signed. * @return true if this is a signed integer data-type */ - public boolean isSigned() { - return signed; - } + public abstract boolean isSigned(); @Override public String getDefaultLabelPrefix() { @@ -134,6 +129,7 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt if (size <= 0) { return null; } + boolean signed = isSigned(); DataOrganization dataOrganization = getDataOrganization(); if (size == dataOrganization.getCharSize()) { return signed ? C_SIGNED_CHAR : C_UNSIGNED_CHAR; @@ -311,7 +307,7 @@ public abstract class AbstractIntegerDataType extends BuiltIn implements ArraySt boolean negative = bigInt.signum() < 0; - if (negative && (!signed || (format != FormatSettingsDefinition.DECIMAL))) { + if (negative && (!isSigned() || (format != FormatSettingsDefinition.DECIMAL))) { // force use of unsigned value bigInt = bigInt.add(BigInteger.valueOf(2).pow(bitLength)); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractSignedIntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractSignedIntegerDataType.java new file mode 100644 index 0000000000..67231bd75e --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractSignedIntegerDataType.java @@ -0,0 +1,37 @@ +/* ### + * 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.model.data; + +/** + * Base type for unsigned integer data types. + */ +public abstract class AbstractSignedIntegerDataType extends AbstractIntegerDataType { + + /** + * Constructor + * + * @param name a signed data-type name (also used as the mnemonic) + * @param dtm data-type manager whose data organization should be used + */ + protected AbstractSignedIntegerDataType(String name, DataTypeManager dtm) { + super(name, dtm); + } + + @Override + public final boolean isSigned() { + return true; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractUnsignedIntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractUnsignedIntegerDataType.java new file mode 100644 index 0000000000..2c03452ea5 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractUnsignedIntegerDataType.java @@ -0,0 +1,37 @@ +/* ### + * 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.model.data; + +/** + * Base type for unsigned integer data types. + */ +public abstract class AbstractUnsignedIntegerDataType extends AbstractIntegerDataType { + + /** + * Constructor + * + * @param name a unsigned data-type name (also used as the mnemonic) + * @param dtm data-type manager whose data organization should be used + */ + protected AbstractUnsignedIntegerDataType(String name, DataTypeManager dtm) { + super(name, dtm); + } + + @Override + public final boolean isSigned() { + return false; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AddressSpaceSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AddressSpaceSettingsDefinition.java index b6177add8b..2f9093c63f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AddressSpaceSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AddressSpaceSettingsDefinition.java @@ -21,8 +21,8 @@ import org.apache.commons.lang3.StringUtils; import ghidra.docking.settings.Settings; import ghidra.docking.settings.StringSettingsDefinition; -import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.ProgramArchitecture; public class AddressSpaceSettingsDefinition implements StringSettingsDefinition, TypeDefSettingsDefinition { @@ -118,12 +118,14 @@ public class AddressSpaceSettingsDefinition @Override public boolean addPreferredValues(Object settingsOwner, Set set) { - if (settingsOwner instanceof ProgramBasedDataTypeManager) { - ProgramBasedDataTypeManager dtm = (ProgramBasedDataTypeManager) settingsOwner; - AddressFactory addressFactory = dtm.getProgram().getAddressFactory(); - for (AddressSpace space : addressFactory.getAllAddressSpaces()) { - if (space.isLoadedMemorySpace()) { - set.add(space.getName()); + if (settingsOwner instanceof DataTypeManager) { + DataTypeManager dtm = (DataTypeManager) settingsOwner; + ProgramArchitecture arch = dtm.getProgramArchitecture(); + if (arch != null) { + for (AddressSpace space : arch.getAddressFactory().getAllAddressSpaces()) { + if (space.isLoadedMemorySpace()) { + set.add(space.getName()); + } } } return true; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java index 1419616ff4..ff7ae47c9b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldPackingImpl.java @@ -20,6 +20,7 @@ import static ghidra.program.model.pcode.ElementId.*; import java.io.IOException; +import ghidra.program.database.DBStringMapAdapter; import ghidra.program.model.pcode.Encoder; import ghidra.util.xml.SpecXmlUtils; import ghidra.xml.XmlElement; @@ -27,9 +28,13 @@ import ghidra.xml.XmlPullParser; public class BitFieldPackingImpl implements BitFieldPacking { - private boolean useMSConvention = false; - private boolean typeAlignmentEnabled = true; - private int zeroLengthBoundary = 0; + public static final boolean DEFAULT_USE_MS_CONVENTION = false; + public static final boolean DEFAULT_TYPE_ALIGNMENT_ENABLED = true; + public static final int DEFAULT_ZERO_LENGTH_BOUNDARY = 0; + + private boolean useMSConvention = DEFAULT_USE_MS_CONVENTION; + private boolean typeAlignmentEnabled = DEFAULT_TYPE_ALIGNMENT_ENABLED; + private int zeroLengthBoundary = DEFAULT_ZERO_LENGTH_BOUNDARY; @Override public boolean useMSConvention() { @@ -76,26 +81,81 @@ public class BitFieldPackingImpl implements BitFieldPacking { } /** - * Write configuration to a stream as a \ element - * @param encoder is the stream encoder - * @throws IOException for errors writing to the underlying stream + * Save the specified bitfield packing options to the specified DB data map. + * @param bitfieldPacking bitfield packing options + * @param dataMap DB data map + * @param keyPrefix key prefix for all map entries + * @throws IOException if an IO error occurs + */ + static void save(BitFieldPacking bitfieldPacking, DBStringMapAdapter dataMap, + String keyPrefix) throws IOException { + + boolean useMSConvention = bitfieldPacking.useMSConvention(); + if (useMSConvention != DEFAULT_USE_MS_CONVENTION) { + dataMap.put(keyPrefix + "use_MS_convention", Boolean.toString(useMSConvention)); + } + + boolean typeAlignmentEnabled = bitfieldPacking.isTypeAlignmentEnabled(); + if (typeAlignmentEnabled != DEFAULT_TYPE_ALIGNMENT_ENABLED) { + dataMap.put(keyPrefix + "type_alignment_enabled", + Boolean.toString(typeAlignmentEnabled)); + } + + int zeroLengthBoundary = bitfieldPacking.getZeroLengthBoundary(); + if (zeroLengthBoundary != DEFAULT_ZERO_LENGTH_BOUNDARY) { + dataMap.put(keyPrefix + "zero_length_boundary", Integer.toString(zeroLengthBoundary)); + } + } + + /** + * Restore a data organization from the specified DB data map. + * @param dataMap DB data map + * @param keyPrefix key prefix for all map entries + * @return data organization + * @throws IOException if an IO error occurs + */ + static BitFieldPackingImpl restore(DBStringMapAdapter dataMap, String keyPrefix) + throws IOException { + + BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); + + bitFieldPacking.useMSConvention = + dataMap.getBoolean(keyPrefix + ELEM_USE_MS_CONVENTION.name(), + bitFieldPacking.useMSConvention); + + bitFieldPacking.typeAlignmentEnabled = dataMap.getBoolean( + keyPrefix + ELEM_TYPE_ALIGNMENT_ENABLED.name(), bitFieldPacking.typeAlignmentEnabled); + + bitFieldPacking.zeroLengthBoundary = + dataMap.getInt(keyPrefix + ELEM_ZERO_LENGTH_BOUNDARY.name(), + bitFieldPacking.zeroLengthBoundary); + + return bitFieldPacking; + } + + /** + * Output the details of this bitfield packing to a encoded document formatter. + * @param encoder the output document encoder. + * @throws IOException if an IO error occurs while encoding/writing output */ public void encode(Encoder encoder) throws IOException { - if (!useMSConvention && typeAlignmentEnabled && zeroLengthBoundary == 0) { + if (useMSConvention == DEFAULT_USE_MS_CONVENTION && + typeAlignmentEnabled == DEFAULT_TYPE_ALIGNMENT_ENABLED && + zeroLengthBoundary == DEFAULT_ZERO_LENGTH_BOUNDARY) { return; // All defaults } encoder.openElement(ELEM_BITFIELD_PACKING); - if (useMSConvention) { + if (useMSConvention != DEFAULT_USE_MS_CONVENTION) { encoder.openElement(ELEM_USE_MS_CONVENTION); encoder.writeBool(ATTRIB_VALUE, true); encoder.closeElement(ELEM_USE_MS_CONVENTION); } - if (!typeAlignmentEnabled) { + if (typeAlignmentEnabled != DEFAULT_TYPE_ALIGNMENT_ENABLED) { encoder.openElement(ELEM_TYPE_ALIGNMENT_ENABLED); encoder.writeBool(ATTRIB_VALUE, false); encoder.closeElement(ELEM_TYPE_ALIGNMENT_ENABLED); } - if (zeroLengthBoundary != 0) { + if (zeroLengthBoundary != DEFAULT_ZERO_LENGTH_BOUNDARY) { encoder.openElement(ELEM_ZERO_LENGTH_BOUNDARY); encoder.writeSignedInteger(ATTRIB_VALUE, zeroLengthBoundary); encoder.closeElement(ELEM_ZERO_LENGTH_BOUNDARY); @@ -115,13 +175,13 @@ public class BitFieldPackingImpl implements BitFieldPacking { String name = subel.getName(); String value = subel.getAttribute("value"); - if (name.equals("use_MS_convention")) { + if (name.equals(ELEM_USE_MS_CONVENTION.name())) { useMSConvention = SpecXmlUtils.decodeBoolean(value); } - else if (name.equals("type_alignment_enabled")) { + else if (name.equals(ELEM_TYPE_ALIGNMENT_ENABLED.name())) { typeAlignmentEnabled = SpecXmlUtils.decodeBoolean(value); } - else if (name.equals("zero_length_boundary")) { + else if (name.equals(ELEM_ZERO_LENGTH_BOUNDARY.name())) { zeroLengthBoundary = SpecXmlUtils.decodeInt(value); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BooleanDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BooleanDataType.java index ca300be5ef..48ba33918a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BooleanDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BooleanDataType.java @@ -26,7 +26,7 @@ import ghidra.program.model.mem.MemoryAccessException; /** * Provides a definition of an Ascii byte in a program. */ -public class BooleanDataType extends AbstractIntegerDataType { +public class BooleanDataType extends AbstractUnsignedIntegerDataType { private static SettingsDefinition[] SETTINGS_DEFS = {}; @@ -40,7 +40,7 @@ public class BooleanDataType extends AbstractIntegerDataType { } public BooleanDataType(DataTypeManager dtm) { - super("bool", false, dtm); + super("bool", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java index 8f4588dad9..094939e06a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BuiltInDataTypeManager.java @@ -15,24 +15,29 @@ */ package ghidra.program.model.data; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import javax.help.UnsupportedOperationException; import javax.swing.event.ChangeListener; import ghidra.framework.ShutdownHookRegistry; import ghidra.framework.ShutdownPriority; +import ghidra.program.database.symbol.VariableStorageManager; +import ghidra.program.model.lang.ProgramArchitecture; import ghidra.util.*; import ghidra.util.classfinder.ClassFilter; import ghidra.util.classfinder.ClassSearcher; import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** * Data type manager for built in types that do not live anywhere except * in memory. */ -public class BuiltInDataTypeManager extends StandAloneDataTypeManager { +public final class BuiltInDataTypeManager extends StandAloneDataTypeManager { // TODO: There appear to be many public methods in DataTypeManagerDB which could potentially modify the // underlying database - these methods should probably be overridden @@ -64,6 +69,17 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager { initialize(); } + protected final void setProgramArchitecture(ProgramArchitecture programArchitecture, + VariableStorageManager variableStorageMgr, boolean force, TaskMonitor monitor) + throws IOException, CancelledException { + throw new UnsupportedOperationException("program architecture change not permitted"); + } + + @Override + protected final boolean isArchitectureChangeAllowed() { + return false; + } + @Override public synchronized int startTransaction(String description) { if (manager != null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java index de99b576dc..95213aa4f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ByteDataType.java @@ -20,9 +20,7 @@ import ghidra.program.model.lang.DecompilerLanguage; /** * Provides a definition of a Byte within a program. */ -public class ByteDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class ByteDataType extends AbstractUnsignedIntegerDataType { /** A statically defined ByteDataType instance.*/ public final static ByteDataType dataType = new ByteDataType(); @@ -32,7 +30,7 @@ public class ByteDataType extends AbstractIntegerDataType { } public ByteDataType(DataTypeManager dtm) { - super("byte", false, dtm); + super("byte", dtm); } @Override @@ -52,8 +50,9 @@ public class ByteDataType extends AbstractIntegerDataType { @Override public String getDecompilerDisplayName(DecompilerLanguage language) { - if (language == DecompilerLanguage.JAVA_LANGUAGE) + if (language == DecompilerLanguage.JAVA_LANGUAGE) { return "ubyte"; + } return name; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java index 38afef95e6..18be5b3488 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharDataType.java @@ -26,7 +26,6 @@ import ghidra.util.classfinder.ClassTranslator; * determined by the data organization of the associated data type manager. */ public class CharDataType extends AbstractIntegerDataType implements DataTypeWithCharset { - private final static long serialVersionUID = 1; static { ClassTranslator.put("ghidra.program.model.data.AsciiDataType", @@ -54,12 +53,13 @@ public class CharDataType extends AbstractIntegerDataType implements DataTypeWit this("char", dtm); } - protected CharDataType(String name, boolean signed, DataTypeManager dtm) { - super(name, signed, dtm); + protected CharDataType(String name, DataTypeManager dtm) { + super(name, dtm); } - private CharDataType(String name, DataTypeManager dtm) { - super(name, isSignedChar(dtm), dtm); + @Override + public boolean isSigned() { + return getDataOrganization().isSignedChar(); } @Override @@ -76,12 +76,6 @@ public class CharDataType extends AbstractIntegerDataType implements DataTypeWit return getLength() != 1; } - private static boolean isSignedChar(DataTypeManager dtm) { - DataOrganization dataOrganization = - dtm != null ? dtm.getDataOrganization() : DataOrganizationImpl.getDefaultOrganization(); - return dataOrganization.isSignedChar(); - } - /** * Returns the C style data-type declaration for this data-type. Null is returned if no * appropriate declaration exists. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java index e404ccfd44..1108770383 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DWordDataType.java @@ -18,7 +18,7 @@ package ghidra.program.model.data; /** * Provides a definition of a Double Word within a program. */ -public class DWordDataType extends AbstractIntegerDataType { +public class DWordDataType extends AbstractUnsignedIntegerDataType { private static final long serialVersionUID = 1L; @@ -30,7 +30,7 @@ public class DWordDataType extends AbstractIntegerDataType { } public DWordDataType(DataTypeManager dtm) { - super("dword", false, dtm); + super("dword", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java index fea8581459..c15d05ba0b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java @@ -164,15 +164,6 @@ public interface DataOrganization { */ int getAlignment(DataType dataType); -// /** -// * Determines the offset where the specified data type should be placed to be properly aligned. -// * @param minimumOffset the minimum allowable offset where the data type can be placed. -// * @param dataType the data type -// * @param dtSize the data type's size -// * @return the aligned offset for the data type -// */ -// int getAlignmentOffset(int minimumOffset, DataType dataType, int dtSize); - /** * Determine if this DataOrganization is equivalent to another specific instance * @param obj is the other instance diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java index fa33af396c..b16292b30d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java @@ -21,6 +21,7 @@ import static ghidra.program.model.pcode.ElementId.*; import java.io.IOException; import java.util.*; +import ghidra.program.database.DBStringMapAdapter; import ghidra.program.model.lang.Language; import ghidra.program.model.pcode.Encoder; import ghidra.util.exception.NoValueException; @@ -34,26 +35,48 @@ import ghidra.xml.XmlPullParser; */ public class DataOrganizationImpl implements DataOrganization { + // NOTE: it is important that defaults match Decompiler defaults + public static final int DEFAULT_MACHINE_ALIGNMENT = 8; + public static final int DEFAULT_DEFAULT_ALIGNMENT = 1; + public static final int DEFAULT_DEFAULT_POINTER_ALIGNMENT = 4; + public static final int DEFAULT_POINTER_SHIFT = 0; + public static final int DEFAULT_POINTER_SIZE = 4; + public static final int DEFAULT_CHAR_SIZE = 1; + public static final boolean DEFAULT_CHAR_IS_SIGNED = true; + public static final int DEFAULT_WIDE_CHAR_SIZE = 2; + public static final int DEFAULT_SHORT_SIZE = 2; + public static final int DEFAULT_INT_SIZE = 4; + public static final int DEFAULT_LONG_SIZE = 4; + public static final int DEFAULT_LONG_LONG_SIZE = 8; + public static final int DEFAULT_FLOAT_SIZE = 4; + public static final int DEFAULT_DOUBLE_SIZE = 8; + public static final int DEFAULT_LONG_DOUBLE_SIZE = 8; + + // DBStringMapAdapter save/restore keys + private static final String BIG_ENDIAN_NAME = "big_endian"; + private static final String SIGNED_CHAR_TYPE_NAME = "signed_char_type"; + private int absoluteMaxAlignment = NO_MAXIMUM_ALIGNMENT; - private int machineAlignment = 8; - private int defaultAlignment = 1; - private int defaultPointerAlignment = 4; + private int machineAlignment = DEFAULT_MACHINE_ALIGNMENT; + private int defaultAlignment = DEFAULT_DEFAULT_ALIGNMENT; + private int defaultPointerAlignment = DEFAULT_DEFAULT_POINTER_ALIGNMENT; // Default sizes for primitive data types. - private int pointerShift = 0; - private int pointerSize = 4; - private int charSize = 1; - private int wideCharSize = 2; - private int shortSize = 2; - private int integerSize = 4; - private int longSize = 4; - private int longLongSize = 8; - private int floatSize = 4; - private int doubleSize = 8; - private int longDoubleSize = 8; + private int pointerShift = DEFAULT_POINTER_SHIFT; + private int pointerSize = DEFAULT_POINTER_SIZE; + private int charSize = DEFAULT_CHAR_SIZE; + private boolean isSignedChar = DEFAULT_CHAR_IS_SIGNED; + private int wideCharSize = DEFAULT_WIDE_CHAR_SIZE; + private int shortSize = DEFAULT_SHORT_SIZE; + private int integerSize = DEFAULT_INT_SIZE; + private int longSize = DEFAULT_LONG_SIZE; + private int longLongSize = DEFAULT_LONG_LONG_SIZE; + private int floatSize = DEFAULT_FLOAT_SIZE; + private int doubleSize = DEFAULT_DOUBLE_SIZE; + private int longDoubleSize = DEFAULT_LONG_DOUBLE_SIZE; + // Endianess explicitly set and not supported by saveXml/restore private boolean bigEndian = false; - private boolean isSignedChar = true; private BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl(); @@ -86,7 +109,9 @@ public class DataOrganizationImpl implements DataOrganization { dataOrganization.setSizeAlignment(4, 4); dataOrganization.setSizeAlignment(8, 4); if (language != null) { + // NOTE: Ensure that saveXml always saves pointer size dataOrganization.setPointerSize(language.getDefaultSpace().getPointerSize()); + // NOTE: Endianess is not handled by saveXml/restore dataOrganization.setBigEndian(language.isBigEndian()); } return dataOrganization; @@ -556,84 +581,300 @@ public class DataOrganizationImpl implements DataOrganization { return (value2 != 0) ? getGreatestCommonDenominator(value2, value1 % value2) : value1; } + /** + * Save the specified data organization to the specified DB data map. + * All existing map entries starting with keyPrefix will be removed prior + * to ading the new map entries. + * @param dataOrg data organization + * @param dataMap DB data map + * @param keyPrefix key prefix for all map entries + * @throws IOException if an IO error occurs + */ + public static void save(DataOrganization dataOrg, DBStringMapAdapter dataMap, String keyPrefix) + throws IOException { + + for (String key : dataMap.keySet()) { + if (key.startsWith(keyPrefix)) { + dataMap.delete(key); + } + } + + if (dataOrg.isBigEndian()) { // default is little-endian + dataMap.put(keyPrefix + BIG_ENDIAN_NAME, Boolean.TRUE.toString()); + } + + int absoluteMaxAlignment = dataOrg.getAbsoluteMaxAlignment(); + if (absoluteMaxAlignment != NO_MAXIMUM_ALIGNMENT) { + dataMap.put(keyPrefix + ELEM_ABSOLUTE_MAX_ALIGNMENT.name(), + Integer.toString(absoluteMaxAlignment)); + } + + int machineAlignment = dataOrg.getMachineAlignment(); + if (machineAlignment != DEFAULT_MACHINE_ALIGNMENT) { + dataMap.put(keyPrefix + ELEM_MACHINE_ALIGNMENT.name(), + Integer.toString(machineAlignment)); + } + + int defaultAlignment = dataOrg.getDefaultAlignment(); + if (defaultAlignment != DEFAULT_DEFAULT_ALIGNMENT) { + dataMap.put(keyPrefix + ELEM_DEFAULT_ALIGNMENT.name(), + Integer.toString(defaultAlignment)); + } + + int defaultPointerAlignment = dataOrg.getDefaultPointerAlignment(); + if (defaultPointerAlignment != DEFAULT_DEFAULT_POINTER_ALIGNMENT) { + dataMap.put(keyPrefix + ELEM_DEFAULT_POINTER_ALIGNMENT.name(), + Integer.toString(defaultPointerAlignment)); + } + + int pointerSize = dataOrg.getPointerSize(); + if (pointerSize != DEFAULT_POINTER_SIZE) { + dataMap.put(keyPrefix + ELEM_POINTER_SIZE.name(), Integer.toString(pointerSize)); + } + + int pointerShift = dataOrg.getPointerShift(); + if (pointerShift != DEFAULT_POINTER_SHIFT) { + dataMap.put(keyPrefix + ELEM_POINTER_SHIFT.name(), Integer.toString(pointerShift)); + } + + boolean isSignedChar = dataOrg.isSignedChar(); + if (!isSignedChar) { + // NOTE: This differs from XML element name + dataMap.put(keyPrefix + SIGNED_CHAR_TYPE_NAME, Boolean.toString(isSignedChar)); + } + + int charSize = dataOrg.getCharSize(); + if (charSize != DEFAULT_CHAR_SIZE) { + dataMap.put(keyPrefix + ELEM_CHAR_SIZE.name(), Integer.toString(charSize)); + } + + int wideCharSize = dataOrg.getWideCharSize(); + if (wideCharSize != DEFAULT_WIDE_CHAR_SIZE) { + dataMap.put(keyPrefix + ELEM_WCHAR_SIZE.name(), Integer.toString(wideCharSize)); + } + + int shortSize = dataOrg.getShortSize(); + if (shortSize != DEFAULT_SHORT_SIZE) { + dataMap.put(keyPrefix + ELEM_SHORT_SIZE.name(), Integer.toString(shortSize)); + } + + int integerSize = dataOrg.getIntegerSize(); + if (integerSize != DEFAULT_INT_SIZE) { + dataMap.put(keyPrefix + ELEM_INTEGER_SIZE.name(), Integer.toString(integerSize)); + } + + int longSize = dataOrg.getLongSize(); + if (longSize != DEFAULT_LONG_SIZE) { + dataMap.put(keyPrefix + ELEM_LONG_SIZE.name(), Integer.toString(longSize)); + } + + int longLongSize = dataOrg.getLongLongSize(); + if (longLongSize != DEFAULT_LONG_LONG_SIZE) { + dataMap.put(keyPrefix + ELEM_LONG_LONG_SIZE.name(), Integer.toString(longLongSize)); + } + + int floatSize = dataOrg.getFloatSize(); + if (floatSize != DEFAULT_FLOAT_SIZE) { + dataMap.put(keyPrefix + ELEM_FLOAT_SIZE.name(), Integer.toString(floatSize)); + } + + int doubleSize = dataOrg.getDoubleSize(); + if (doubleSize != DEFAULT_DOUBLE_SIZE) { + dataMap.put(keyPrefix + ELEM_DOUBLE_SIZE.name(), Integer.toString(doubleSize)); + } + + int longDoubleSize = dataOrg.getLongDoubleSize(); + if (longDoubleSize != DEFAULT_LONG_DOUBLE_SIZE) { + dataMap.put(keyPrefix + ELEM_LONG_DOUBLE_SIZE.name(), Integer.toString(longDoubleSize)); + } + + for (int size : dataOrg.getSizes()) { + try { + String key = keyPrefix + ELEM_SIZE_ALIGNMENT_MAP.name() + "." + size; + dataMap.put(key, Integer.toString(dataOrg.getSizeAlignment(size))); + } + catch (NoValueException e) { + // skip entry + } + } + + BitFieldPackingImpl.save(dataOrg.getBitFieldPacking(), dataMap, + keyPrefix + ELEM_BITFIELD_PACKING.name() + "."); + } + + /** + * Restore a data organization from the specified DB data map. + * @param dataMap DB data map + * @param keyPrefix key prefix for all map entries + * @return data organization + * @throws IOException if an IO error occurs + */ + public static DataOrganizationImpl restore(DBStringMapAdapter dataMap, String keyPrefix) + throws IOException { + + DataOrganizationImpl dataOrg = new DataOrganizationImpl(); + + dataOrg.bigEndian = dataMap.getBoolean(BIG_ENDIAN_NAME, false); + + dataOrg.absoluteMaxAlignment = + dataMap.getInt(keyPrefix + ELEM_ABSOLUTE_MAX_ALIGNMENT.name(), + dataOrg.absoluteMaxAlignment); + + dataOrg.machineAlignment = + dataMap.getInt(keyPrefix + ELEM_MACHINE_ALIGNMENT.name(), dataOrg.machineAlignment); + + dataOrg.defaultAlignment = + dataMap.getInt(keyPrefix + ELEM_DEFAULT_ALIGNMENT.name(), dataOrg.defaultAlignment); + + dataOrg.defaultPointerAlignment = + dataMap.getInt(keyPrefix + ELEM_DEFAULT_POINTER_ALIGNMENT.name(), + dataOrg.defaultPointerAlignment); + + dataOrg.pointerSize = + dataMap.getInt(keyPrefix + ELEM_POINTER_SIZE.name(), dataOrg.pointerSize); + + dataOrg.pointerShift = + dataMap.getInt(keyPrefix + ELEM_POINTER_SHIFT.name(), dataOrg.pointerShift); + + dataOrg.isSignedChar = + dataMap.getBoolean(keyPrefix + SIGNED_CHAR_TYPE_NAME, dataOrg.isSignedChar); + + dataOrg.charSize = dataMap.getInt(keyPrefix + ELEM_CHAR_SIZE.name(), dataOrg.charSize); + + dataOrg.wideCharSize = + dataMap.getInt(keyPrefix + ELEM_WCHAR_SIZE.name(), dataOrg.wideCharSize); + + dataOrg.shortSize = dataMap.getInt(keyPrefix + ELEM_SHORT_SIZE.name(), dataOrg.shortSize); + + dataOrg.integerSize = + dataMap.getInt(keyPrefix + ELEM_INTEGER_SIZE.name(), dataOrg.integerSize); + + dataOrg.longSize = dataMap.getInt(keyPrefix + ELEM_LONG_SIZE.name(), dataOrg.longSize); + + dataOrg.longLongSize = + dataMap.getInt(keyPrefix + ELEM_LONG_LONG_SIZE.name(), dataOrg.longLongSize); + + dataOrg.floatSize = dataMap.getInt(keyPrefix + ELEM_FLOAT_SIZE.name(), dataOrg.floatSize); + + dataOrg.doubleSize = + dataMap.getInt(keyPrefix + ELEM_DOUBLE_SIZE.name(), dataOrg.doubleSize); + + dataOrg.longDoubleSize = + dataMap.getInt(keyPrefix + ELEM_LONG_DOUBLE_SIZE.name(), dataOrg.longDoubleSize); + + boolean firstEntry = true; + String alignmentMapKeyPrefix = keyPrefix + ELEM_SIZE_ALIGNMENT_MAP.name() + "."; + for (String key : dataMap.keySet()) { + if (!key.startsWith(alignmentMapKeyPrefix)) { + continue; + } + try { + int size = Integer.valueOf(key.substring(alignmentMapKeyPrefix.length())); + int alignment = Integer.valueOf(dataMap.get(key)); + if (firstEntry) { + dataOrg.sizeAlignmentMap.clear(); + firstEntry = false; + } + dataOrg.sizeAlignmentMap.put(size, alignment); + } + catch (NumberFormatException e) { + // ignore + } + } + + dataOrg.bitFieldPacking = + BitFieldPackingImpl.restore(dataMap, keyPrefix + ELEM_BITFIELD_PACKING.name() + "."); + + return dataOrg; + } + + /** + * Output the details of this data organization to a encoded document formatter. + * @param encoder the output document encoder. + * @throws IOException if an IO error occurs while encoding/writing output + */ public void encode(Encoder encoder) throws IOException { encoder.openElement(ELEM_DATA_ORGANIZATION); + + // NOTE: endianess intentionally omitted from output + if (absoluteMaxAlignment != NO_MAXIMUM_ALIGNMENT) { encoder.openElement(ELEM_ABSOLUTE_MAX_ALIGNMENT); encoder.writeSignedInteger(ATTRIB_VALUE, absoluteMaxAlignment); encoder.closeElement(ELEM_ABSOLUTE_MAX_ALIGNMENT); } - if (machineAlignment != 8) { + if (machineAlignment != DEFAULT_MACHINE_ALIGNMENT) { encoder.openElement(ELEM_MACHINE_ALIGNMENT); encoder.writeSignedInteger(ATTRIB_VALUE, machineAlignment); encoder.closeElement(ELEM_MACHINE_ALIGNMENT); } - if (defaultAlignment != 1) { + if (defaultAlignment != DEFAULT_DEFAULT_ALIGNMENT) { encoder.openElement(ELEM_DEFAULT_ALIGNMENT); encoder.writeSignedInteger(ATTRIB_VALUE, defaultAlignment); encoder.closeElement(ELEM_DEFAULT_ALIGNMENT); } - if (defaultPointerAlignment != 4) { + if (defaultPointerAlignment != DEFAULT_DEFAULT_POINTER_ALIGNMENT) { encoder.openElement(ELEM_DEFAULT_POINTER_ALIGNMENT); encoder.writeSignedInteger(ATTRIB_VALUE, defaultPointerAlignment); encoder.closeElement(ELEM_DEFAULT_POINTER_ALIGNMENT); } - if (pointerSize != 0) { - encoder.openElement(ELEM_POINTER_SIZE); - encoder.writeSignedInteger(ATTRIB_VALUE, pointerSize); - encoder.closeElement(ELEM_POINTER_SIZE); - } - if (pointerShift != 0) { + + // Always output pointer size + encoder.openElement(ELEM_POINTER_SIZE); + encoder.writeSignedInteger(ATTRIB_VALUE, pointerSize); + encoder.closeElement(ELEM_POINTER_SIZE); + + if (pointerShift != DEFAULT_POINTER_SHIFT) { encoder.openElement(ELEM_POINTER_SHIFT); encoder.writeSignedInteger(ATTRIB_VALUE, pointerShift); encoder.closeElement(ELEM_POINTER_SHIFT); } - if (!isSignedChar) { + if (isSignedChar != DEFAULT_CHAR_IS_SIGNED) { encoder.openElement(ELEM_CHAR_TYPE); - encoder.writeBool(ATTRIB_SIGNED, false); + encoder.writeBool(ATTRIB_SIGNED, isSignedChar); encoder.closeElement(ELEM_CHAR_TYPE); } - if (charSize != 1) { + if (charSize != DEFAULT_CHAR_SIZE) { encoder.openElement(ELEM_CHAR_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, charSize); encoder.closeElement(ELEM_CHAR_SIZE); } - if (wideCharSize != 2) { + if (wideCharSize != DEFAULT_WIDE_CHAR_SIZE) { encoder.openElement(ELEM_WCHAR_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, wideCharSize); encoder.closeElement(ELEM_WCHAR_SIZE); } - if (shortSize != 2) { + if (shortSize != DEFAULT_SHORT_SIZE) { encoder.openElement(ELEM_SHORT_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, shortSize); encoder.closeElement(ELEM_SHORT_SIZE); } - if (integerSize != 4) { + if (integerSize != DEFAULT_INT_SIZE) { encoder.openElement(ELEM_INTEGER_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, integerSize); encoder.closeElement(ELEM_INTEGER_SIZE); } - if (longSize != 4) { + if (longSize != DEFAULT_LONG_SIZE) { encoder.openElement(ELEM_LONG_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, longSize); encoder.closeElement(ELEM_LONG_SIZE); } - if (longLongSize != 8) { + if (longLongSize != DEFAULT_LONG_LONG_SIZE) { encoder.openElement(ELEM_LONG_LONG_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, longLongSize); encoder.closeElement(ELEM_LONG_LONG_SIZE); } - if (floatSize != 4) { + if (floatSize != DEFAULT_FLOAT_SIZE) { encoder.openElement(ELEM_FLOAT_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, floatSize); encoder.closeElement(ELEM_FLOAT_SIZE); } - if (doubleSize != 8) { + if (doubleSize != DEFAULT_DOUBLE_SIZE) { encoder.openElement(ELEM_DOUBLE_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, doubleSize); encoder.closeElement(ELEM_DOUBLE_SIZE); } - if (longDoubleSize != 8) { + if (longDoubleSize != DEFAULT_LONG_DOUBLE_SIZE) { encoder.openElement(ELEM_LONG_DOUBLE_SIZE); encoder.writeSignedInteger(ATTRIB_VALUE, longDoubleSize); encoder.closeElement(ELEM_LONG_DOUBLE_SIZE); @@ -654,33 +895,39 @@ public class DataOrganizationImpl implements DataOrganization { } /** - * Restore settings from an XML stream. This expects to see a \ tag. - * The XML is designed to override existing default settings. So this object needs to - * be pre-populated with defaults, typically via getDefaultOrganization(). + * Restore settings from an XML stream. This expects to see parser positioned on the + * <data_organization> start tag. The XML is designed to override existing language-specific + * default settings which are pre-populated with {@link #getDefaultOrganization(Language)}. This + * will will ensure that the endianess setting is properly established since it is not included + * in the XML. * @param parser is the XML stream */ public void restoreXml(XmlPullParser parser) { + + // NOTE: endianess intentionally omitted from XML. + parser.start(); while (parser.peek().isStart()) { String name = parser.peek().getName(); - if (name.equals("char_type")) { + if (name.equals(ELEM_CHAR_TYPE.name())) { XmlElement subel = parser.start(); - String boolStr = subel.getAttribute("signed"); - isSignedChar = SpecXmlUtils.decodeBoolean(boolStr); + String boolStr = subel.getAttribute(ATTRIB_SIGNED.name()); + isSignedChar = SpecXmlUtils.decodeBoolean(boolStr, isSignedChar); parser.end(subel); continue; } - else if (name.equals("bitfield_packing")) { + else if (name.equals(ELEM_BITFIELD_PACKING.name())) { bitFieldPacking.restoreXml(parser); continue; } - else if (name.equals("size_alignment_map")) { + else if (name.equals(ELEM_SIZE_ALIGNMENT_MAP.name())) { XmlElement subel = parser.start(); while (parser.peek().isStart()) { XmlElement subsubel = parser.start(); - int size = SpecXmlUtils.decodeInt(subsubel.getAttribute("size")); - int alignment = SpecXmlUtils.decodeInt(subsubel.getAttribute("alignment")); + int size = SpecXmlUtils.decodeInt(subsubel.getAttribute(ATTRIB_SIZE.name())); + int alignment = + SpecXmlUtils.decodeInt(subsubel.getAttribute(ATTRIB_ALIGNMENT.name())); sizeAlignmentMap.put(size, alignment); parser.end(subsubel); } @@ -689,51 +936,51 @@ public class DataOrganizationImpl implements DataOrganization { } XmlElement subel = parser.start(); - String value = subel.getAttribute("value"); + String value = subel.getAttribute(ATTRIB_VALUE.name()); - if (name.equals("absolute_max_alignment")) { + if (name.equals(ELEM_ABSOLUTE_MAX_ALIGNMENT.name())) { absoluteMaxAlignment = SpecXmlUtils.decodeInt(value); } - else if (name.equals("machine_alignment")) { + else if (name.equals(ELEM_MACHINE_ALIGNMENT.name())) { machineAlignment = SpecXmlUtils.decodeInt(value); } - else if (name.equals("default_alignment")) { + else if (name.equals(ELEM_DEFAULT_ALIGNMENT.name())) { defaultAlignment = SpecXmlUtils.decodeInt(value); } - else if (name.equals("default_pointer_alignment")) { + else if (name.equals(ELEM_DEFAULT_POINTER_ALIGNMENT.name())) { defaultPointerAlignment = SpecXmlUtils.decodeInt(value); } - else if (name.equals("pointer_size")) { + else if (name.equals(ELEM_POINTER_SIZE.name())) { pointerSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("pointer_shift")) { + else if (name.equals(ELEM_POINTER_SHIFT.name())) { pointerShift = SpecXmlUtils.decodeInt(value); } - else if (name.equals("char_size")) { + else if (name.equals(ELEM_CHAR_SIZE.name())) { charSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("wchar_size")) { + else if (name.equals(ELEM_WCHAR_SIZE.name())) { wideCharSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("short_size")) { + else if (name.equals(ELEM_SHORT_SIZE.name())) { shortSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("integer_size")) { + else if (name.equals(ELEM_INTEGER_SIZE.name())) { integerSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("long_size")) { + else if (name.equals(ELEM_LONG_SIZE.name())) { longSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("long_long_size")) { + else if (name.equals(ELEM_LONG_LONG_SIZE.name())) { longLongSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("float_size")) { + else if (name.equals(ELEM_FLOAT_SIZE.name())) { floatSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("double_size")) { + else if (name.equals(ELEM_DOUBLE_SIZE.name())) { doubleSize = SpecXmlUtils.decodeInt(value); } - else if (name.equals("long_double_size")) { + else if (name.equals(ELEM_LONG_DOUBLE_SIZE.name())) { longDoubleSize = SpecXmlUtils.decodeInt(value); } parser.end(subel); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java index 9738ee9cb3..e077d58298 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeArchiveIdDumper.java @@ -39,6 +39,7 @@ public class DataTypeArchiveIdDumper implements GhidraLaunchable { FileWriter writer = new FileWriter(outputFile); FileDataTypeManager archive = FileDataTypeManager.openFileArchive(archiveFile, false); + archive.reportWarning(); UniversalID universalID2 = archive.getUniversalID(); writer.write("FILE_ID: " + Long.toHexString(universalID2.getValue())); writer.write("\n"); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java index 8fc90407c0..3d806e962c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java @@ -18,6 +18,11 @@ package ghidra.program.model.data; import java.util.*; import db.Transaction; +import ghidra.program.database.SpecExtension; +import ghidra.program.database.map.AddressMap; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.CancelledException; @@ -59,6 +64,21 @@ public interface DataTypeManager { */ public UniversalID getUniversalID(); + /** + * Get the optional program architecture details associated with this archive + * @return program architecture details or null if none + */ + public ProgramArchitecture getProgramArchitecture(); + + /** + * Get the program architecture information which has been associated with this + * datatype manager. If {@link #getProgramArchitecture()} returns null this method + * may still return information if the program architecture was set on an archive but unable + * to properly instantiate. + * @return program architecture summary if it has been set + */ + public String getProgramArchitectureSummary(); + /** * Returns true if the given category path exists in this datatype manager * @param path the path @@ -138,6 +158,12 @@ public interface DataTypeManager { */ public Iterator getAllComposites(); + /** + * Returns an iterator over all function definition data types in this manager + * @return the iterator + */ + public Iterator getAllFunctionDefinitions(); + /** * Begin searching at the root category for all data types with the * given name. Places all the data types in this data type manager @@ -522,6 +548,13 @@ public interface DataTypeManager { */ public DataOrganization getDataOrganization(); + /** + * Returns the associated AddressMap used by this datatype manager. + * @return the AddressMap used by this datatype manager or null if + * one has not be established. + */ + public AddressMap getAddressMap(); + /** * Returns a list of source archives not including the builtin or the program's archive. * @return a list of source archives not including the builtin or the program's archive. @@ -568,4 +601,49 @@ public interface DataTypeManager { * @return true if BuiltIn Settings are permitted */ public boolean allowsDefaultComponentSettings(); + + /** + * Get the ordered list of known calling convention names. The reserved names + * "unknown" and "default" are not included. The returned collection will include all names + * ever used or resolved by associated {@link Function} and {@link FunctionDefinition} objects, + * even if not currently defined by the associated {@link CompilerSpec} or {@link Program} + * {@link SpecExtension}. To get only those calling conventions formally defined, the method + * {@link CompilerSpec#getCallingConventions()} should be used. + * + * @return all known calling convention names. + */ + public Collection getKnownCallingConventionNames(); + + /** + * Get the ordered list of defined calling convention names. The reserved names + * "unknown" and "default" are not included. The returned collection may not include all names + * referenced by various functions and function-definitions. This set is generally limited to + * those defined by the associated compiler specification. If this instance does not have an + * assigned architecture the {@link GenericCallingConvention} names will be returned. + *

+ * For a set of all known names (including those that are not defined by compiler spec) + * see {@link #getKnownCallingConventionNames()}. + * + * @return the set of defined calling convention names. + */ + public Collection getDefinedCallingConventionNames(); + + /** + * Get the default calling convention's prototype model in this datatype manager if known. + * + * @return the default calling convention prototype model or null. + */ + public PrototypeModel getDefaultCallingConvention(); + + /** + * Get the prototype model of the calling convention with the specified name from the + * associated compiler specification. If an architecture has not been established this method + * will return null. If {@link Function#DEFAULT_CALLING_CONVENTION_STRING} + * is specified {@link #getDefaultCallingConvention()} will be returned. + * + * @param name the calling convention name + * @return the named function calling convention prototype model or null. + */ + public PrototypeModel getCallingConvention(String name); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListener.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListener.java index 400955df3f..c1791265f4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListener.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListener.java @@ -123,4 +123,11 @@ public interface DataTypeManagerChangeListener { */ public void sourceArchiveAdded(final DataTypeManager dataTypeManager, final SourceArchive sourceArchive); + + /** + * Notification that the program architecture associated with the specified + * dataTypeManager has changed. + * @param dataTypeManager data type manager referring to the given source information. + */ + public void programArchitectureChanged(DataTypeManager dataTypeManager); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerAdapter.java index 82be93f86d..d55f93374d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerAdapter.java @@ -73,4 +73,8 @@ public class DataTypeManagerChangeListenerAdapter implements DataTypeManagerChan public void sourceArchiveChanged(DataTypeManager dataTypeManager, SourceArchive dataTypeSource) { } + @Override + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerHandler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerHandler.java index db82316e47..f5765860f9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerHandler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManagerChangeListenerHandler.java @@ -15,18 +15,20 @@ */ package ghidra.program.model.data; -import ghidra.util.datastruct.WeakDataStructureFactory; -import ghidra.util.datastruct.WeakSet; - import java.io.IOException; import java.io.ObjectInputStream; import javax.swing.SwingUtilities; +import ghidra.util.datastruct.WeakDataStructureFactory; +import ghidra.util.datastruct.WeakSet; + /** * - * Default implementation for a category change listener that sends out the - * events to its own list of category change listeners. + * Default implementation for a {@link DataTypeManagerChangeListener} that sends out the + * events to its own list of listeners. + * + * NOTE: all listener notifications must be asynchronous within a different thread. * */ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChangeListener { @@ -51,158 +53,122 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan } @Override - public void categoryAdded(final DataTypeManager dtm, final CategoryPath path) { + public void categoryAdded(DataTypeManager dtm, CategoryPath path) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.categoryAdded(dtm, path); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.categoryAdded(dtm, path); } - }; - invokeRunnable(r); + }); } @Override - public void categoryMoved(final DataTypeManager dtm, final CategoryPath oldPath, - final CategoryPath newPath) { + public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, + CategoryPath newPath) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.categoryMoved(dtm, oldPath, newPath); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.categoryMoved(dtm, oldPath, newPath); } - }; - invokeRunnable(r); + }); } @Override - public void categoryRemoved(final DataTypeManager dtm, final CategoryPath path) { + public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.categoryRemoved(dtm, path); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.categoryRemoved(dtm, path); } - }; - invokeRunnable(r); + }); } @Override - public void categoryRenamed(final DataTypeManager dtm, final CategoryPath oldPath, - final CategoryPath newPath) { + public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, + CategoryPath newPath) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.categoryRenamed(dtm, oldPath, newPath); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.categoryRenamed(dtm, oldPath, newPath); } - }; - invokeRunnable(r); + }); } @Override - public void dataTypeAdded(final DataTypeManager dtm, final DataTypePath path) { + public void dataTypeAdded(DataTypeManager dtm, DataTypePath path) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.dataTypeAdded(dtm, path); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.dataTypeAdded(dtm, path); } - }; - invokeRunnable(r); + }); } @Override - public void dataTypeChanged(final DataTypeManager dtm, final DataTypePath path) { + public void dataTypeChanged(DataTypeManager dtm, DataTypePath path) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.dataTypeChanged(dtm, path); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.dataTypeChanged(dtm, path); } - }; - invokeRunnable(r); + }); } @Override - public void dataTypeMoved(final DataTypeManager dtm, final DataTypePath oldPath, - final DataTypePath newPath) { + public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.dataTypeMoved(dtm, oldPath, newPath); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.dataTypeMoved(dtm, oldPath, newPath); } - }; - invokeRunnable(r); + }); } @Override - public void dataTypeRemoved(final DataTypeManager dtm, final DataTypePath path) { + public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.dataTypeRemoved(dtm, path); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.dataTypeRemoved(dtm, path); } - }; - invokeRunnable(r); + }); } @Override - public void dataTypeRenamed(final DataTypeManager dtm, final DataTypePath oldPath, - final DataTypePath newPath) { + public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.dataTypeRenamed(dtm, oldPath, newPath); - listener.favoritesChanged(dtm, oldPath, false); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.dataTypeRenamed(dtm, oldPath, newPath); + listener.favoritesChanged(dtm, oldPath, false); } - }; - invokeRunnable(r); + }); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { @@ -220,73 +186,67 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan } @Override - public void dataTypeReplaced(final DataTypeManager dtm, final DataTypePath oldPath, - final DataTypePath newPath, final DataType newDataType) { + public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, + DataTypePath newPath, DataType newDataType) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.dataTypeReplaced(dtm, oldPath, newPath, newDataType); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.dataTypeReplaced(dtm, oldPath, newPath, newDataType); } - }; - invokeRunnable(r); + }); } @Override - public void favoritesChanged(final DataTypeManager dtm, final DataTypePath path, - final boolean isFavorite) { + public void favoritesChanged(DataTypeManager dtm, DataTypePath path, boolean isFavorite) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.favoritesChanged(dtm, path, isFavorite); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.favoritesChanged(dtm, path, isFavorite); } - }; - invokeRunnable(r); + }); } @Override - public void sourceArchiveChanged(final DataTypeManager dataTypeManager, - final SourceArchive dataTypeSource) { + public void sourceArchiveChanged(DataTypeManager dataTypeManager, + SourceArchive dataTypeSource) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.sourceArchiveChanged(dataTypeManager, dataTypeSource); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.sourceArchiveChanged(dataTypeManager, dataTypeSource); } - }; - invokeRunnable(r); + }); } @Override - public void sourceArchiveAdded(final DataTypeManager dataTypeManager, - final SourceArchive dataTypeSource) { + public void sourceArchiveAdded(DataTypeManager dataTypeManager, + SourceArchive dataTypeSource) { if (listenerList.isEmpty()) { return; } - Runnable r = new Runnable() { - @Override - public void run() { - for (DataTypeManagerChangeListener listener : listenerList) { - listener.sourceArchiveAdded(dataTypeManager, dataTypeSource); - } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.sourceArchiveAdded(dataTypeManager, dataTypeSource); } - }; - invokeRunnable(r); + }); + } + + public void programArchitectureChanged(DataTypeManager dataTypeManager) { + if (listenerList.isEmpty()) { + return; + } + invokeRunnable(() -> { + for (DataTypeManagerChangeListener listener : listenerList) { + listener.programArchitectureChanged(dataTypeManager); + } + }); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java index 6a1fed94d9..1a9323c165 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java @@ -22,6 +22,8 @@ import db.DBConstants; import generic.jar.ResourceFile; import ghidra.framework.store.db.PackedDBHandle; import ghidra.framework.store.db.PackedDatabase; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.lang.Language; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.*; @@ -48,15 +50,21 @@ public class FileDataTypeManager extends StandAloneDataTypeManager /** * Construct a new DataTypeFileManager using the default data organization. + *

+ * NOTE: it may be appropriate to {@link #getWarning() check for warnings} after + * opening an existing archive file prior to use. While an archive will remain useable + * with a warning condition, architecture-specific data may not be available or up-to-date. + * * @param packedDbfile file to load or create based upon openMode * @param openMode one of the DBConstants: CREATE, UPDATE, READ_ONLY, UPGRADE - * @throws IOException + * @throws IOException if an IO error occurs */ private FileDataTypeManager(ResourceFile packedDbfile, int openMode) throws IOException { super(validateFilename(packedDbfile), openMode); file = packedDbfile; name = getRootName(file.getName()); packedDB = ((PackedDBHandle) dbHandle).getPackedDatabase(); + reportWarning(); } private static ResourceFile validateFilename(ResourceFile packedDbfile) { @@ -70,18 +78,27 @@ public class FileDataTypeManager extends StandAloneDataTypeManager * Create a new data-type file archive using the default data organization * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @return data-type manager backed by specified packedDbFile - * @throws IOException + * @throws IOException if an IO error occurs */ public static FileDataTypeManager createFileArchive(File packedDbfile) throws IOException { return new FileDataTypeManager(new ResourceFile(packedDbfile), DBConstants.CREATE); } /** - * Open an existing data-type file archive using the default data organization + * Open an existing data-type file archive using the default data organization. + *

+ * NOTE: If archive has an assigned architecture, issues may arise due to a revised or + * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not + * prevent the archive from being opened. Such a warning condition will ne logged and may + * result in missing or stale information for existing datatypes which have architecture related + * data. In some case it may be appropriate to + * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive + * object prior to its use. + * * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @param openForUpdate if true archive will be open for update * @return data-type manager backed by specified packedDbFile - * @throws IOException + * @throws IOException if an IO error occurs */ public static FileDataTypeManager openFileArchive(File packedDbfile, boolean openForUpdate) throws IOException { @@ -89,11 +106,20 @@ public class FileDataTypeManager extends StandAloneDataTypeManager } /** - * Open an existing data-type file archive using the default data organization + * Open an existing data-type file archive using the default data organization. + *

+ * NOTE: If archive has an assigned architecture, issues may arise due to a revised or + * missing {@link Language}/{@link CompilerSpec} which will result in a warning but not + * prevent the archive from being opened. Such a warning condition will ne logged and may + * result in missing or stale information for existing datatypes which have architecture related + * data. In some case it may be appropriate to + * {@link FileDataTypeManager#getWarning() check for warnings} on the returned archive + * object prior to its use. + * * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) * @param openForUpdate if true archive will be open for update * @return data-type manager backed by specified packedDbFile - * @throws IOException + * @throws IOException if an IO error occurs */ public static FileDataTypeManager openFileArchive(ResourceFile packedDbfile, boolean openForUpdate) throws IOException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinition.java index c1c90571c2..7ce46edeca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinition.java @@ -16,7 +16,9 @@ package ghidra.program.model.data; import ghidra.program.model.listing.FunctionSignature; +import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.SourceType; +import ghidra.util.exception.InvalidInputException; /** * Defines a function signature for things like function pointers. @@ -49,12 +51,44 @@ public interface FunctionDefinition extends DataType, FunctionSignature { */ public void setVarArgs(boolean hasVarArgs); + /** + * Set whether or not this function has a return. + * + * @param hasNoReturn true if this function does not return. + */ + public void setNoReturn(boolean hasNoReturn); + /** * Set the generic calling convention associated with this function definition. + *
+ * The total number of unique calling convention names used within a given {@link Program} + * or {@link DataTypeManager} may be limited (e.g., 127). When this limit is exceeded an error + * will be logged and this setting ignored. + * * @param genericCallingConvention generic calling convention + * @deprecated Use of {@link GenericCallingConvention} is deprecated since arbitrary calling + * convention names are now supported. {@link #setCallingConvention(String)} should be used. */ public void setGenericCallingConvention(GenericCallingConvention genericCallingConvention); + /** + * Set the calling convention associated with this function definition. + *
+ * The total number of unique calling convention names used within a given {@link Program} + * or {@link DataTypeManager} may be limited (e.g., 127). When this limit is exceeded an error + * will be logged and this setting ignored. + * + * @param conventionName calling convention name or null. This name is restricted to those + * defined by {@link GenericCallingConvention}, the associated compiler specification. + * The prototype model declaration name form (e.g., "__stdcall") should be specified as it + * appears in a compiler specification (*.cspec). The special "unknown" and "default" names + * are also allowed. + * @throws InvalidInputException if specified conventionName is not defined by + * {@link GenericCallingConvention} or the associated compiler specification if + * datatype manager has an associated program architecture. + */ + public void setCallingConvention(String conventionName) throws InvalidInputException; + /** * Replace the given argument with another data type * diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinitionDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinitionDataType.java index 4e518c3108..7a407f70f3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinitionDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FunctionDefinitionDataType.java @@ -19,11 +19,12 @@ import java.util.ArrayList; import ghidra.docking.settings.Settings; import ghidra.program.database.data.DataTypeUtilities; -import ghidra.program.model.lang.PrototypeModel; +import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.symbol.SourceType; import ghidra.util.UniversalID; +import ghidra.util.exception.InvalidInputException; /** * Definition of a function for things like function pointers. @@ -34,7 +35,8 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct private ParameterDefinition[] params; private String comment; private boolean hasVarArgs; - private GenericCallingConvention genericCallingConvention = GenericCallingConvention.unknown; + private boolean hasNoReturn; + private String callingConventionName = CompilerSpec.CALLING_CONVENTION_unknown; public FunctionDefinitionDataType(String name) { this(CategoryPath.ROOT, name, null, null); @@ -108,15 +110,9 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct params = paramList.toArray(new ParameterDefinition[paramList.size()]); hasVarArgs = function.hasVarArgs(); + hasNoReturn = function.hasNoReturn(); - PrototypeModel prototypeModel = function.getCallingConvention(); - - if (prototypeModel == null) { - genericCallingConvention = GenericCallingConvention.unknown; - } - else { - genericCallingConvention = prototypeModel.getGenericCallingConvention(); - } + callingConventionName = function.getCallingConventionName(); } private ParameterDefinition getParameterDefinition(Parameter param, boolean useFormalType, @@ -140,7 +136,8 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct setReturnType(rtnType.clone(getDataTypeManager())); setArguments(sig.getArguments()); hasVarArgs = sig.hasVarArgs(); - genericCallingConvention = sig.getGenericCallingConvention(); + hasNoReturn = sig.hasNoReturn(); + callingConventionName = sig.getCallingConventionName(); } @Override @@ -169,14 +166,58 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct } @Override - public void setGenericCallingConvention(GenericCallingConvention genericCallingConvention) { - this.genericCallingConvention = genericCallingConvention; + public void setNoReturn(boolean hasNoReturn) { + this.hasNoReturn = hasNoReturn; } @Override - public GenericCallingConvention getGenericCallingConvention() { - return genericCallingConvention != null ? genericCallingConvention - : GenericCallingConvention.unknown; + public void setGenericCallingConvention(GenericCallingConvention genericCallingConvention) { + this.callingConventionName = genericCallingConvention.getDeclarationName(); + } + + @Override + public void setCallingConvention(String conventionName) throws InvalidInputException { + + if (conventionName == null || + CompilerSpec.CALLING_CONVENTION_unknown.equals(conventionName)) { + this.callingConventionName = CompilerSpec.CALLING_CONVENTION_unknown; + return; + } + if (CompilerSpec.CALLING_CONVENTION_default.equals(conventionName)) { + this.callingConventionName = CompilerSpec.CALLING_CONVENTION_default; + return; + } + + if (GenericCallingConvention + .getGenericCallingConvention(conventionName) != GenericCallingConvention.unknown) { + ProgramArchitecture arch = dataMgr != null ? dataMgr.getProgramArchitecture() : null; + if (arch != null) { + CompilerSpec compilerSpec = arch.getCompilerSpec(); + PrototypeModel callingConvention = + compilerSpec.getCallingConvention(conventionName); + if (callingConvention == null) { + throw new InvalidInputException( + "Invalid calling convention name: " + conventionName); + } + } + } + + this.callingConventionName = conventionName; + } + + @Override + public PrototypeModel getCallingConvention() { + ProgramArchitecture arch = dataMgr != null ? dataMgr.getProgramArchitecture() : null; + if (arch == null) { + return null; + } + CompilerSpec compilerSpec = arch.getCompilerSpec(); + return compilerSpec.getCallingConvention(callingConventionName); + } + + @Override + public String getCallingConventionName() { + return callingConventionName; } @Override @@ -227,11 +268,15 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct @Override public String getPrototypeString(boolean includeCallingConvention) { StringBuffer buf = new StringBuffer(); + if (includeCallingConvention && hasNoReturn) { + buf.append(NORETURN_DISPLAY_STRING); + buf.append(" "); + } buf.append((returnType != null ? returnType.getDisplayName() : "void")); buf.append(" "); if (includeCallingConvention && - genericCallingConvention != GenericCallingConvention.unknown) { - buf.append(genericCallingConvention.name()); + !Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(callingConventionName)) { + buf.append(callingConventionName); buf.append(" "); } buf.append(name); @@ -279,6 +324,11 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct return hasVarArgs; } + @Override + public boolean hasNoReturn() { + return hasNoReturn; + } + /** * Compare the comment of the given function signature to my comment. * @@ -316,7 +366,8 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct (DataTypeUtilities.isSameOrEquivalentDataType(signature.getReturnType(), this.returnType)) && (hasVarArgs == signature.hasVarArgs()) && - (genericCallingConvention == signature.getGenericCallingConvention())) { + (hasNoReturn == signature.hasNoReturn()) && + callingConventionName.equals(signature.getCallingConventionName())) { ParameterDefinition[] args = signature.getArguments(); if (args.length == this.params.length) { for (int i = 0; i < args.length; i++) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericCallingConvention.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericCallingConvention.java index cdd9ab0248..cecd440ecb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericCallingConvention.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericCallingConvention.java @@ -21,6 +21,10 @@ import ghidra.program.model.lang.CompilerSpec; * GenericCallingConvention identifies the generic calling convention * associated with a specific function definition. This can be used to help identify * the appropriate compiler-specific function prototype (i.e., calling convention). + * + * @deprecated Calling convention name strings should be used instead of this class. + * {@link CompilerSpec} provides constants for those included in this enumeration and other + * setter/getter methods exist for using the string form. */ public enum GenericCallingConvention { @@ -76,14 +80,11 @@ public enum GenericCallingConvention { /** * Returns the GenericCallingConvention corresponding to the specified - * type string or unknown. Case and underscore prefix is ignored. - * @param callingConvention calling convention name - * @return GenericCallingConvention + * type string or unknown if name is not defined. + * @param callingConvention calling convention declaration name (e.g., "__stdcall") + * @return GenericCallingConvention or {@link #unknown} if not found. */ public static GenericCallingConvention getGenericCallingConvention(String callingConvention) { - while (callingConvention.startsWith("_")) { - callingConvention = callingConvention.substring(1); - } for (GenericCallingConvention value : GenericCallingConvention.values()) { if (value.name().equalsIgnoreCase(callingConvention)) { return value; @@ -92,28 +93,6 @@ public enum GenericCallingConvention { return unknown; } - /** - * Returns the GenericCallingConvention which is likely to correspond with the - * specified prototype name. - * @param callingConvention compiler specific calling convention name - * @return GenericCallingConvention - */ - public static GenericCallingConvention guessFromName(String callingConvention) { - if (callingConvention == null) { - return unknown; - } - callingConvention = callingConvention.toLowerCase(); - for (GenericCallingConvention value : GenericCallingConvention.values()) { - if (value == unknown) { - continue; - } - if (callingConvention.contains(value.name())) { - return value; - } - } - return unknown; - } - /** * Returns the GenericCallingConvention corresponding to the specified * ordinal. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java index 132959c615..f37e2d1f90 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer16DataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * A fixed size 16 byte signed integer (commonly referred to in C as int128_t) */ -public class Integer16DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class Integer16DataType extends AbstractSignedIntegerDataType { /** A statically defined Integer16DataType instance.*/ public final static Integer16DataType dataType = new Integer16DataType(); @@ -30,7 +28,7 @@ public class Integer16DataType extends AbstractIntegerDataType { } public Integer16DataType(DataTypeManager dtm) { - super("int16", true, dtm); + super("int16", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java index 36b682d5b2..cab694606d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer3DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class Integer3DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class Integer3DataType extends AbstractSignedIntegerDataType { /** A statically defined Integer3DataType instance.*/ public final static Integer3DataType dataType = new Integer3DataType(); @@ -27,7 +25,7 @@ public class Integer3DataType extends AbstractIntegerDataType { } public Integer3DataType(DataTypeManager dtm) { - super("int3", true, dtm); + super("int3", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java index 8be233505a..6cec0672ea 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer5DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class Integer5DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class Integer5DataType extends AbstractSignedIntegerDataType { /** A statically defined Integer5DataType instance.*/ public final static Integer5DataType dataType = new Integer5DataType(); @@ -27,7 +25,7 @@ public class Integer5DataType extends AbstractIntegerDataType { } public Integer5DataType(DataTypeManager dtm) { - super("int5", true, dtm); + super("int5", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java index fe4733b0af..9761bed8b5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer6DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class Integer6DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class Integer6DataType extends AbstractSignedIntegerDataType { /** A statically defined Integer6DataType instance.*/ public final static Integer6DataType dataType = new Integer6DataType(); @@ -27,7 +25,7 @@ public class Integer6DataType extends AbstractIntegerDataType { } public Integer6DataType(DataTypeManager dtm) { - super("int6", true, dtm); + super("int6", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java index 135a74f0c5..eacb1dd002 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Integer7DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class Integer7DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class Integer7DataType extends AbstractSignedIntegerDataType { /** A statically defined Integer7DataType instance.*/ public final static Integer7DataType dataType = new Integer7DataType(); @@ -27,7 +25,7 @@ public class Integer7DataType extends AbstractIntegerDataType { } public Integer7DataType(DataTypeManager dtm) { - super("int7", true, dtm); + super("int7", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java index 61f713916c..10e0019cf6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IntegerDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for an signed Integer dataType */ -public class IntegerDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class IntegerDataType extends AbstractSignedIntegerDataType { /** A statically defined IntegerDataType instance.*/ public final static IntegerDataType dataType = new IntegerDataType(); @@ -30,7 +28,7 @@ public class IntegerDataType extends AbstractIntegerDataType { } public IntegerDataType(DataTypeManager dtm) { - super("int", true, dtm); + super("int", dtm); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java index 5c310a809e..48796e6b94 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for a Signed Long Integer dataType */ -public class LongDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class LongDataType extends AbstractSignedIntegerDataType { /** A statically defined LongDataType instance.*/ public final static LongDataType dataType = new LongDataType(); @@ -30,29 +28,19 @@ public class LongDataType extends AbstractIntegerDataType { } public LongDataType(DataTypeManager dtm) { - super("long", true, dtm); + super("long", dtm); } - /** - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getLongSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Signed Long Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java index 0c8b95dce4..a913b56067 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LongLongDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for an Signed LongLong Integer dataType */ -public class LongLongDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class LongLongDataType extends AbstractSignedIntegerDataType { /** A statically defined LongLongDataType instance.*/ public final static LongLongDataType dataType = new LongLongDataType(); @@ -30,29 +28,19 @@ public class LongLongDataType extends AbstractIntegerDataType { } public LongLongDataType(DataTypeManager dtm) { - super("longlong", true, dtm); + super("longlong", dtm); } - /** - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getLongLongSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Signed Long Long Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramArchitectureTranslator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramArchitectureTranslator.java new file mode 100644 index 0000000000..67a42c6043 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramArchitectureTranslator.java @@ -0,0 +1,69 @@ +/* ### + * 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.model.data; + +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.program.util.LanguageTranslatorAdapter; + +public class ProgramArchitectureTranslator extends LanguageTranslatorAdapter { + + private CompilerSpec oldCompilerSpec; + private CompilerSpec newCompilerSpec; + + public ProgramArchitectureTranslator(Language oldLanguage, CompilerSpecID oldCompilerSpecId, + Language newLanguage, CompilerSpecID newCompilerSpecId) + throws CompilerSpecNotFoundException, IncompatibleLanguageException { + super(oldLanguage, newLanguage); + if (!oldLanguage.getProcessor().equals(newLanguage.getProcessor())) { + throw new IncompatibleLanguageException("Architecture processors differ: " + + oldLanguage.getProcessor() + " vs " + newLanguage.getProcessor()); + } + this.oldCompilerSpec = oldLanguage.getCompilerSpecByID(oldCompilerSpecId); + this.newCompilerSpec = newLanguage.getCompilerSpecByID(newCompilerSpecId); + validateDefaultSpaceMap(); + } + + public ProgramArchitectureTranslator(LanguageID oldLanguageId, int oldLanguageVersion, + CompilerSpecID oldCompilerSpecId, Language newLanguage, + CompilerSpecID newCompilerSpecId) + throws LanguageNotFoundException, CompilerSpecNotFoundException, + IncompatibleLanguageException { + this(getLanguage(oldLanguageId, oldLanguageVersion), oldCompilerSpecId, newLanguage, + newCompilerSpecId); + } + + private static Language getLanguage(LanguageID languageId, int languageVersion) + throws LanguageNotFoundException { + Language language = DefaultLanguageService.getLanguageService().getLanguage(languageId); + if (languageVersion > 0 && language.getVersion() != languageVersion) { + throw new LanguageNotFoundException( + "Language not found for '" + languageId + "' version " + languageVersion + ".x"); + } + return language; + } + + public CompilerSpec getOldCompilerSpec() { + return oldCompilerSpec; + } + + public CompilerSpec getNewCompilerSpec() { + return newCompilerSpec; + } + + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java index de85f83a9a..6de4d09765 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/QWordDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Provides a definition of a Quad Word within a program. */ -public class QWordDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class QWordDataType extends AbstractUnsignedIntegerDataType { /** A statically defined QWordDataType instance.*/ public final static QWordDataType dataType = new QWordDataType(); @@ -30,7 +28,7 @@ public class QWordDataType extends AbstractIntegerDataType { } public QWordDataType(DataTypeManager dtm) { - super("qword", false, dtm); + super("qword", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java index dd1ac9e3e4..83ce54f9ba 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShortDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for a Short Integer dataType */ -public class ShortDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class ShortDataType extends AbstractSignedIntegerDataType { /** A statically defined ShortDataType instance.*/ public final static ShortDataType dataType = new ShortDataType(); @@ -30,29 +28,19 @@ public class ShortDataType extends AbstractIntegerDataType { } public ShortDataType(DataTypeManager dtm) { - super("short", true, dtm); + super("short", dtm); } - /** - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getShortSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Signed Short Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java index 50b96dd33b..f41f444b13 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedByteDataType.java @@ -20,9 +20,7 @@ import ghidra.program.model.lang.DecompilerLanguage; /** * Provides a definition of a Signed Byte within a program. */ -public class SignedByteDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class SignedByteDataType extends AbstractSignedIntegerDataType { /** A statically defined SignedByteDataType instance.*/ public final static SignedByteDataType dataType = new SignedByteDataType(); @@ -32,7 +30,7 @@ public class SignedByteDataType extends AbstractIntegerDataType { } public SignedByteDataType(DataTypeManager dtm) { - super("sbyte", true, dtm); + super("sbyte", dtm); } @Override @@ -52,8 +50,9 @@ public class SignedByteDataType extends AbstractIntegerDataType { @Override public String getDecompilerDisplayName(DecompilerLanguage language) { - if (language == DecompilerLanguage.JAVA_LANGUAGE) + if (language == DecompilerLanguage.JAVA_LANGUAGE) { return "byte"; + } return name; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java index e79ef3bcf3..2ef633df67 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedCharDataType.java @@ -22,7 +22,6 @@ package ghidra.program.model.data; * associated data type manager. */ public class SignedCharDataType extends CharDataType { - private final static long serialVersionUID = 1; public static final SignedCharDataType dataType = new SignedCharDataType(); @@ -34,7 +33,12 @@ public class SignedCharDataType extends CharDataType { } public SignedCharDataType(DataTypeManager dtm) { - super("schar", true, dtm); + super("schar", dtm); + } + + @Override + public boolean isSigned() { + return true; } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java index b1e5e1a3de..fbcf50ebd2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedDWordDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Provides a definition of a Signed Double Word within a program. */ -public class SignedDWordDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class SignedDWordDataType extends AbstractSignedIntegerDataType { /** A statically defined SignedDWordDataType instance.*/ public final static SignedDWordDataType dataType = new SignedDWordDataType(); @@ -30,7 +28,7 @@ public class SignedDWordDataType extends AbstractIntegerDataType { } public SignedDWordDataType(DataTypeManager dtm) { - super("sdword", true, dtm); + super("sdword", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java index 1a2800f0f8..c81df3d093 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedQWordDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Provides a definition of a Signed Quad Word within a program. */ -public class SignedQWordDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class SignedQWordDataType extends AbstractSignedIntegerDataType { /** A statically defined SignedQWordDataType instance.*/ public final static SignedQWordDataType dataType = new SignedQWordDataType(); @@ -30,7 +28,7 @@ public class SignedQWordDataType extends AbstractIntegerDataType { } public SignedQWordDataType(DataTypeManager dtm) { - super("sqword", true, dtm); + super("sqword", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java index 7e909ce939..ed6dad3ef0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/SignedWordDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Provides a basic implementation of a signed word datatype */ -public class SignedWordDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class SignedWordDataType extends AbstractSignedIntegerDataType { /** A statically defined SignedWordDataType instance.*/ public final static SignedWordDataType dataType = new SignedWordDataType(); @@ -30,7 +28,7 @@ public class SignedWordDataType extends AbstractIntegerDataType { } public SignedWordDataType(DataTypeManager dtm) { - super("sword", true, dtm); + super("sword", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java index 9ee458c209..e25c6d4b99 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java @@ -15,26 +15,100 @@ */ package ghidra.program.model.data; +import java.io.Closeable; import java.io.IOException; import java.util.LinkedList; -import db.Transaction; -import db.DBConstants; +import javax.help.UnsupportedOperationException; + +import com.google.common.collect.ImmutableList; + +import db.*; +import db.util.ErrorHandler; import generic.jar.ResourceFile; +import ghidra.framework.store.LockException; +import ghidra.program.database.DBStringMapAdapter; +import ghidra.program.database.ProgramAddressFactory; import ghidra.program.database.data.DataTypeManagerDB; -import ghidra.util.InvalidNameException; +import ghidra.program.database.symbol.VariableStorageManager; +import ghidra.program.database.symbol.VariableStorageManagerDB; +import ghidra.program.model.address.AddressFactory; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.program.util.LanguageTranslator; +import ghidra.util.*; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; /** * Basic implementation of the DataTypeManger interface */ -public class StandAloneDataTypeManager extends DataTypeManagerDB { +public class StandAloneDataTypeManager extends DataTypeManagerDB implements Closeable { + + private static final String LANGUAGE_VERSION = "Language Version"; // major version only + private static final String LANGUAGE_ID = "Language ID"; + private static final String COMPILER_SPEC_ID = "Compiler Spec ID"; - protected String name; private int transactionCount; private Long transaction; private boolean commitTransaction; + private LanguageTranslator languageUpgradeTranslator; + private String programArchitectureSummary; // summary of expected program architecture + + protected String name; + + public static enum ArchiveWarning { + /** + * {@link #NONE} indicates a normal archive condition + */ + NONE, + + /** + * {@link #UPGRADED_LANGUAGE_VERSION} indicates an archive which has been open for update + * was upgraded to a newer language version. This is expected when the {@link Language} + * required by the associated {@link ProgramArchitecture} has a major version change + * which involves significant {@link Register} changes. Sharing an upgraded archive + * may impact others who do not have access to the updated {@link Language} module. + */ + UPGRADED_LANGUAGE_VERSION, + + // programArchitectureSummary must be set for the warnings below + + /** + * {@link #LANGUAGE_NOT_FOUND} indicates the {@link Language} or its appropriate version, + * required by the associated {@link ProgramArchitecture}, was not found or encountered + * a problem being loaded. The {@link FileDataTypeManager#getWarningDetail()} may provide + * additional insight to the underlying cause. + */ + LANGUAGE_NOT_FOUND, + + /** + * {@link #COMPILER_SPEC_NOT_FOUND} indicates the {@link CompilerSpec}, + * required by the associated {@link ProgramArchitecture}, was not found or encountered + * a problem being loaded. The {@link FileDataTypeManager#getWarningDetail()} may provide + * additional insight to the underlying cause. This condition can only occur if the + * required {@link Language} was found. + */ + COMPILER_SPEC_NOT_FOUND, + + /** + * {@link #LANGUAGE_UPGRADE_REQURED} indicates an archive which has been open read-only + * requires an upgraded to a newer language version. This is expected when the + * {@link Language} required by the associated {@link ProgramArchitecture} has a major + * version change within the current installation. Major version changes for a + * {@link Language} rarely occur but are required when significant {@link Register} + * or addressing changes have been made. Upgrading a shared archive may impact others + * who do not have access to the updated {@link Language} module and should be + * coordinated with others who may be affected. + */ + LANGUAGE_UPGRADE_REQURED, + } + + private ArchiveWarning warning; + private Exception warningDetail; + /** * Constructor for new temporary data-type manager using the default DataOrganization. * Note that this manager does not support the save or saveAs operation. @@ -59,6 +133,12 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { /** * Constructor for a data-type manager backed by a packed database file. * When opening for UPDATE an automatic upgrade will be performed if required. + *

+ * NOTE: {@link #reportWarning()} should be invoked immediately after + * instantiating a {@link StandAloneDataTypeManager} for an existing database after + * {@link #getName()} and {@link #getPath()} can be invoked safely. In addition, it + * may be appropriate to use {@link #getWarning() check for warnings} prior to use. + * * @param packedDbfile packed datatype archive file (i.e., *.gdt resource). * @param openMode open mode CREATE, READ_ONLY or UPDATE (see {@link DBConstants}) * @throws IOException a low-level IO error. This exception may also be thrown @@ -69,6 +149,579 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { super(packedDbfile, openMode); } + /** + * Constructor for a data-type manager using a specified DBHandle. + *

+ * NOTE: {@link #reportWarning()} should be invoked immediately after + * instantiating a {@link StandAloneDataTypeManager} for an existing database after + * {@link #getName()} and {@link #getPath()} can be invoked safely. In addition, it + * may be appropriate to use {@link #getWarning() check for warnings} prior to use. + * + * @param handle open database handle + * @param openMode the program open mode + * @param errHandler the database I/O error handler + * @param lock the program synchronization lock + * @param monitor the progress monitor + * @throws CancelledException if the user cancels an upgrade + * @throws VersionException if the database does not match the expected version. + * @throws IOException if a database I/O error occurs. + */ + protected StandAloneDataTypeManager(DBHandle handle, int openMode, ErrorHandler errHandler, + Lock lock, TaskMonitor monitor) + throws CancelledException, VersionException, IOException { + super(handle, null, openMode, null, errHandler, lock, monitor); + } + + /** + * Get the {@link ArchiveWarning} which may have occured immediately following + * instatiation of this {@link StandAloneDataTypeManager}. {@link ArchiveWarning#NONE} + * will be returned if not warning condition. + * @return warning type. + */ + public ArchiveWarning getWarning() { + return warning; + } + + /** + * Get the detail exception associated with {@link ArchiveWarning#LANGUAGE_NOT_FOUND} or + * {@link ArchiveWarning#COMPILER_SPEC_NOT_FOUND} warning (see {@link #getWarning()}) + * immediately following instatiation of this {@link StandAloneDataTypeManager}. + * @return warning detail exception or null + */ + public Exception getWarningDetail() { + return warningDetail; + } + + /** + * Due to the supression of error and warning conditions during instantiation this method should + * be invoked at the end of instatiation when {@link #getName()} and {@link #getPath()} are + * ready to be invoked safely. Logging will be performed via {@link Msg}. + */ + protected void reportWarning() { + String msg; + switch (warning) { + case NONE: + break; + case LANGUAGE_NOT_FOUND: + msg = "Language not found for Archive '" + getName() + "': " + + warningDetail.getMessage(); + Msg.error(this, msg); + break; + case COMPILER_SPEC_NOT_FOUND: + msg = "Compiler specification not found for Archive '" + getName() + "': " + + warningDetail.getMessage(); + Msg.error(this, msg); + break; + case LANGUAGE_UPGRADE_REQURED: + msg = "Language upgrade required for Archive '" + getName() + "': " + + programArchitectureSummary; + Msg.warn(this, msg); + break; + case UPGRADED_LANGUAGE_VERSION: + ProgramArchitecture arch = getProgramArchitecture(); + LanguageDescription languageDescription = + arch.getLanguage().getLanguageDescription(); + msg = + "Upgraded program-architecture for Archive: '" + getName() + + "'\n Language: " + + languageDescription.getLanguageID() + " Version " + + languageDescription.getVersion() + ".x" + + ", CompilerSpec: " + arch.getCompilerSpec().getCompilerSpecID(); + Msg.info(this, msg); + } + + } + + @Override + protected void initializeOtherAdapters(int openMode, TaskMonitor monitor) + throws CancelledException, IOException, VersionException { + + warning = ArchiveWarning.NONE; + if (openMode == DBConstants.CREATE) { + return; // optional program architecture is set after initialization is complete + } + + // Check for optional program architecture data (LanguageID, etc.) + // The DB data map is also used by the base implementation to store the data organization + DBStringMapAdapter dataMap = getDataMap(false); + if (dataMap == null) { + return; + } + + String languageIdStr = dataMap.get(LANGUAGE_ID); + if (languageIdStr == null) { + return; // assume architecture info is missing + } + LanguageID languageId = new LanguageID(languageIdStr); + CompilerSpecID compilerSpecId = new CompilerSpecID(dataMap.get(COMPILER_SPEC_ID)); + int languageVersion = 1; + try { + languageVersion = Integer.parseInt(dataMap.get(LANGUAGE_VERSION)); + } + catch (Exception e) { + // Ignore + } + + VariableStorageManagerDB variableStorageMgr = null; + if (VariableStorageManagerDB.exists(dbHandle)) { + variableStorageMgr = + new VariableStorageManagerDB(dbHandle, null, openMode, errHandler, lock, monitor); + } + + programArchitectureSummary = + getProgramArchitectureSummary(languageId, languageVersion, compilerSpecId); + + Language language = null; + LanguageVersionException languageVersionExc = null; + try { + language = DefaultLanguageService.getLanguageService().getLanguage(languageId); + languageVersionExc = + LanguageVersionException.check(language, languageVersion, -1); // don't care about minor version + } + catch (LanguageNotFoundException e) { + warning = ArchiveWarning.LANGUAGE_NOT_FOUND; + warningDetail = e; + try { + languageVersionExc = + LanguageVersionException.checkForLanguageChange(e, languageId, languageVersion); + } + catch (LanguageNotFoundException e2) { + // Missing language or language translation + warningDetail = e2; + return; // allow archive to open without error + } + } + + if (languageVersionExc != null && !languageVersionExc.isUpgradable()) { + // Inability to translate language treated like language-not-found + warning = ArchiveWarning.LANGUAGE_NOT_FOUND; + warningDetail = languageVersionExc; + } + else if (languageVersionExc != null) { + warning = ArchiveWarning.LANGUAGE_UPGRADE_REQURED; + languageUpgradeTranslator = languageVersionExc.getLanguageTranslator(); + + // language upgrade required + if (openMode == DBConstants.READ_ONLY) { + // read-only mode - do not set program architecture - upgrade flag has been set + return; + } + + if (openMode == DBConstants.UPDATE) { + throw languageVersionExc; + } + + // else UPGRADE mode falls-through + language = languageUpgradeTranslator.getNewLanguage(); + compilerSpecId = languageUpgradeTranslator.getNewCompilerSpecID(compilerSpecId); + } + + assert (language != null); + + CompilerSpec compilerSpec; + try { + compilerSpec = language.getCompilerSpecByID(compilerSpecId); + } + catch (CompilerSpecNotFoundException e) { + warning = ArchiveWarning.COMPILER_SPEC_NOT_FOUND; + warningDetail = e; + return; // allow archive to open without error + } + + if (warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED) { + + if (variableStorageMgr != null) { + variableStorageMgr.setLanguage(languageUpgradeTranslator, monitor); + } + + // update data map with language upgrade info + dataMap.put(LANGUAGE_ID, language.getLanguageID().getIdAsString()); + dataMap.put(LANGUAGE_VERSION, Integer.toString(language.getVersion())); + dataMap.put(COMPILER_SPEC_ID, compilerSpecId.getIdAsString()); + + warning = ArchiveWarning.UPGRADED_LANGUAGE_VERSION; + } + + programArchitectureSummary = null; // not needed + + final Language lang = language; + final CompilerSpec cspec = compilerSpec; + final AddressFactory addrFactory = new ProgramAddressFactory(lang, cspec); + + super.setProgramArchitecture(new ProgramArchitecture() { + + @Override + public Language getLanguage() { + return lang; + } + + @Override + public CompilerSpec getCompilerSpec() { + return cspec; + } + + @Override + public AddressFactory getAddressFactory() { + return addrFactory; + } + }, variableStorageMgr, false, monitor); + + if (variableStorageMgr != null) { + variableStorageMgr.setProgramArchitecture(getProgramArchitecture()); + } + + } + + /** + * Get the program architecture information which has been associated with this + * datatype manager. If {@link #getProgramArchitecture()} returns null this method + * may still return information if the program architecture was set on an archive + * and either {@link #isProgramArchitectureMissing()} or + * {@link #isProgramArchitectureUpgradeRequired()} returns true. + * @return program architecture summary if it has been set + */ + @Override + public String getProgramArchitectureSummary() { + if (programArchitectureSummary != null) { + return programArchitectureSummary; + } + return super.getProgramArchitectureSummary(); + } + + /** + * Delete all program architecture related data in response to an + * architecture change when all related data should be removed. + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled + */ + private void deleteAllProgramArchitectureData(TaskMonitor monitor) + throws IOException, CancelledException { + + clearCustomStorageUse(monitor); + + DBStringMapAdapter dataMap = getDataMap(false); + if (dataMap != null) { + dataMap.delete(LANGUAGE_ID); + dataMap.delete(LANGUAGE_VERSION); + dataMap.delete(COMPILER_SPEC_ID); + } + + VariableStorageManagerDB.delete(dbHandle); + + warning = ArchiveWarning.NONE; + programArchitectureSummary = null; + } + + private VariableStorageManagerDB createProgramArchitectureData( + ProgramArchitecture programArchitecture, VariableStorageManagerDB variableStorageMgr) + throws IOException { + Language newLanguage = programArchitecture.getLanguage(); + LanguageID newLanguageId = newLanguage.getLanguageID(); + CompilerSpec newCompilerSpec = programArchitecture.getCompilerSpec(); + CompilerSpecID newCmpilerSpecID = newCompilerSpec.getCompilerSpecID(); + + DBStringMapAdapter dataMap = getDataMap(true); + dataMap.put(LANGUAGE_ID, newLanguageId.getIdAsString()); + dataMap.put(COMPILER_SPEC_ID, newCmpilerSpecID.getIdAsString()); + dataMap.put(LANGUAGE_VERSION, Integer.toString(newLanguage.getVersion())); // major version only + + if (variableStorageMgr == null) { // TODO: may re-use if translation performed + try { + variableStorageMgr = new VariableStorageManagerDB(dbHandle, null, + DBConstants.CREATE, errHandler, lock, TaskMonitor.DUMMY); + variableStorageMgr.setProgramArchitecture(programArchitecture); + } + catch (VersionException | CancelledException e) { + throw new AssertException(e); // unexpected + } + } + + variableStorageMgr.setProgramArchitecture(programArchitecture); + + warning = ArchiveWarning.NONE; + programArchitectureSummary = null; + + return variableStorageMgr; + } + + /** + * Indicates that an program architecture upgrade is required in order + * to constitute associated data. If true, the associated archive + * must be open for update to allow the upgrade to complete, or a new + * program architecture may be set/cleared if such an operation is supported. + * @return true if a program architecture upgrade is required, else false + */ + public boolean isProgramArchitectureUpgradeRequired() { + return warning == ArchiveWarning.LANGUAGE_UPGRADE_REQURED; + } + + /** + * Indicates that a failure occured establishing the program architecture + * for the associated archive. + * @return true if a failure occured establishing the program architecture + */ + public boolean isProgramArchitectureMissing() { + return warning == ArchiveWarning.LANGUAGE_NOT_FOUND || + warning == ArchiveWarning.COMPILER_SPEC_NOT_FOUND; + } + + + /** + * Clear the program architecture setting and all architecture-specific data from this archive. + * Archive will revert to using the default {@link DataOrganization}. + * Archive must be open for update for this method to be used. + * @param monitor task monitor + * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer + * stable and should be closed without saving. + * @throws IOException if IO error occurs + * @throws LockException failure if exclusive access is required + * @throws UnsupportedOperationException if architecture change is not permitted by + * implementation (e.g., {@link BuiltInDataTypeManager}). + */ + public void clearProgramArchitecture(TaskMonitor monitor) + throws CancelledException, IOException, LockException { + lock.acquire(); + try { + + if (!isArchitectureChangeAllowed()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted"); + } + + if (!dbHandle.canUpdate()) { + throw new ReadOnlyException("Read-only Archive: " + getName()); + } + + if (getProgramArchitecture() == null && !isProgramArchitectureMissing()) { + return; + } + + Msg.info(this, "Removing program-architecture for Archive: " + getName()); + + int txId = startTransaction("Remove Program Architecture"); + try { + if (!isArchitectureChangeAllowed()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted"); + } + deleteAllProgramArchitectureData(monitor); + + super.setProgramArchitecture((ProgramArchitecture) null, null, true, monitor); + } + finally { + // TODO: ensure state is restored if transaction rollback/cancel occurs + endTransaction(txId, !monitor.isCancelled()); + } + + defaultListener.programArchitectureChanged(this); + } + finally { + invalidateCache(); + lock.release(); + } + } + + public static enum LanguageUpdateOption { + /** + * All existing storage data should be cleared + */ + CLEAR, + /** + * An attempt should be made to translate from old-to-new language. + * This has limitations (i.e., similar architecture) and may result in + * poor register mappings. + */ + TRANSLATE, + /** + * Variable storage data will be retained as-is but may not de-serialize + * properly when used. + */ + UNCHANGED // TODO: Need to test to see if this option is safe and useable + } + + /** + * Establish the program architecture for this datatype manager. The current setting can be + * determined from {@link #getProgramArchitecture()}. Archive must be open for update for + * this method to be used. + * @param language language + * @param compilerSpecId compiler specification ID defined by the language. + * @param updateOption indicates how variable storage data should be transitioned. If {@link #isProgramArchitectureMissing()} + * is true and {@link LanguageUpdateOption#TRANSLATE} specified, the translator will be based on whatever language version can + * be found. In this situation it may be best to force a {@link LanguageUpdateOption#CLEAR}. + * @param monitor task monitor (cancel not permitted to avoid corrupt state) + * @throws CompilerSpecNotFoundException if invalid compilerSpecId specified for language + * @throws LanguageNotFoundException if current language is not found (if required for data transition) + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled. If thrown, this data type manager is no longer + * stable and should be closed without saving. + * @throws LockException failure if exclusive access is required + * @throws UnsupportedOperationException if architecture change is not permitted + * @throws IncompatibleLanguageException if translation requested but not possible due to incompatible language architectures + */ + public void setProgramArchitecture(Language language, CompilerSpecID compilerSpecId, + LanguageUpdateOption updateOption, TaskMonitor monitor) + throws CompilerSpecNotFoundException, LanguageNotFoundException, IOException, + CancelledException, LockException, UnsupportedOperationException, + IncompatibleLanguageException { + + lock.acquire(); + try { + + if (!isArchitectureChangeAllowed()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted"); + } + + if (!dbHandle.canUpdate()) { + throw new ReadOnlyException("Read-only Archive: " + getName()); + } + + Msg.info(this, + "Updating program-architecture for Archive: " + getName() + "\n Language: " + + language.getLanguageID() + " version " + language.getVersion() + ".x" + + ", CompilerSpec: " + compilerSpecId); + + CompilerSpec compilerSpec = language.getCompilerSpecByID(compilerSpecId); + + // This type of datatype manager only uses VariableStorageManagerDB + VariableStorageManagerDB variableStorageMgr = + (VariableStorageManagerDB) getVariableStorageManager(); + + int txId = startTransaction("Set Program Architecture"); + try { + ProgramArchitectureTranslator translator = null; + + ProgramArchitecture oldArch = getProgramArchitecture(); + if (oldArch != null || isProgramArchitectureMissing()) { + + if (updateOption == LanguageUpdateOption.CLEAR) { + deleteAllProgramArchitectureData(monitor); + variableStorageMgr = null; + } + else if (isProgramArchitectureMissing()) { + + assert (variableStorageMgr == null); + + if (updateOption == LanguageUpdateOption.TRANSLATE) { + // Go out on a limb and use any version of old language if available + DBStringMapAdapter dataMap = getDataMap(false); + LanguageID oldLanguageId = + new LanguageID(getDataMap(false).get(LANGUAGE_ID)); + CompilerSpecID oldCompilerSpecId = + new CompilerSpecID(dataMap.get(COMPILER_SPEC_ID)); + translator = new ProgramArchitectureTranslator(oldLanguageId, -1, + oldCompilerSpecId, language, compilerSpecId); + } + + if (VariableStorageManagerDB.exists(dbHandle)) { + try { + variableStorageMgr = new VariableStorageManagerDB(dbHandle, null, + DBConstants.UPDATE, errHandler, lock, monitor); + } + catch (VersionException e) { + throw new IOException( + "Unexpected version error for VariableStorageManagerDB"); + } + } + } + else if (updateOption == LanguageUpdateOption.TRANSLATE) { + translator = new ProgramArchitectureTranslator(oldArch.getLanguage(), + oldArch.getCompilerSpec().getCompilerSpecID(), language, + compilerSpecId); + } + + if (translator != null && variableStorageMgr != null) { + variableStorageMgr.setLanguage(translator, monitor); + } + } + + ProgramArchitecture programArchitecture = new ProgramArchitecture() { + + @Override + public Language getLanguage() { + return language; + } + + @Override + public CompilerSpec getCompilerSpec() { + return compilerSpec; + } + + @Override + public AddressFactory getAddressFactory() { + return language.getAddressFactory(); + } + }; + + variableStorageMgr = + createProgramArchitectureData(programArchitecture, variableStorageMgr); + + super.setProgramArchitecture(programArchitecture, variableStorageMgr, true, + monitor); + } + finally { + // TODO: ensure state is restored if transaction rollback/cancel occurs + endTransaction(txId, !monitor.isCancelled()); + } + + defaultListener.programArchitectureChanged(this); + } + finally { + invalidateCache(); + lock.release(); + } + } + + /** + * Set the architecture-specific details associated with a new datatype manager. + * This method is intended to be used during instantiation of derived implementations. + * @param programArchitecture program architecture details (required) + * @param variableStorageMgr variable storage manager. Must be null. + * @param store if true database update will occur and datatypes will be updated if + * any change to the data organization is detected (a stored copy may be used to + * detect this condition). This should never be passed as true if opened read-only. + * If true and no variable storage is specified it will be created. + * @param monitor task monitor + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled + * @throws UnsupportedOperationException if language was previously set + */ + @Override + protected void setProgramArchitecture(ProgramArchitecture programArchitecture, + VariableStorageManager variableStorageMgr, boolean store, TaskMonitor monitor) + throws IOException, CancelledException { + + // TODO: Determine if cancelling can leave in bad state + + if (programArchitecture == null) { + throw new IllegalArgumentException("ProgramArchitecture must be specified"); + } + if (variableStorageMgr != null) { + throw new IllegalArgumentException("VariableStorageManager may not be specified"); + } + + if (getProgramArchitecture() != null || isProgramArchitectureUpgradeRequired() || + isProgramArchitectureMissing()) { + throw new UnsupportedOperationException( + "Program-architecture change not permitted with this method"); + } + + if (store) { + variableStorageMgr = createProgramArchitectureData(programArchitecture, null); + } + + // super handles possible change to data organization and update if store is true + super.setProgramArchitecture(programArchitecture, variableStorageMgr, store, monitor); + } + + /** + * Determine if a program architecture change is permitted + * @return true if change allowed else false if disallowed + */ + protected boolean isArchitectureChangeAllowed() { + return true; + } + @Override public String getName() { return name; @@ -83,12 +736,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT); } - - @Override - public final DataOrganization getDataOrganization() { - return super.getDataOrganization(); - } - + @Override public Transaction openTransaction(String description) throws IllegalStateException { return new Transaction() { @@ -173,8 +821,13 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { return null; } + /** + * Get the path name associated with the storage of this stand alone + * datatype manager. + * @return path name or null if not applicable + */ @Override - protected String getPath() { + public String getPath() { return null; } @@ -182,4 +835,30 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB { public ArchiveType getType() { return ArchiveType.TEST; } + + /** + * Update custom storage for function definitions to be unassigned. + * @param monitor task monitor + * @throws CancelledException if task cancelled + */ + private void clearCustomStorageUse(TaskMonitor monitor) throws CancelledException { + + // Get copy of all function defs to avoid concurrent modification of underlaying table + ImmutableList defs = ImmutableList.copyOf(getAllFunctionDefinitions()); + + monitor.initialize(defs.size()); + monitor.setMessage("Clear custom storage use..."); + +// for (FunctionDefinition def : ImmutableList.copyOf(getAllFunctionDefinitions())) { + monitor.checkCanceled(); +// monitor.incrementProgress(1); +// +// // TODO: update function definition +// if (def.hasCustomStorage()) { +// +// +// } +// } + + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java index cc088cf221..1b9f27d0ad 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedCharDataType.java @@ -34,7 +34,12 @@ public class UnsignedCharDataType extends CharDataType { } public UnsignedCharDataType(DataTypeManager dtm) { - super("uchar", false, dtm); + super("uchar", dtm); + } + + @Override + public boolean isSigned() { + return false; } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java index 883f75815d..09bb37dc20 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger16DataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * A fixed size 16 byte unsigned integer (commonly referred to in C as uint128_t) */ -public class UnsignedInteger16DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class UnsignedInteger16DataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedInteger16DataType instance.*/ public final static UnsignedInteger16DataType dataType = new UnsignedInteger16DataType(); @@ -30,7 +28,7 @@ public class UnsignedInteger16DataType extends AbstractIntegerDataType { } public UnsignedInteger16DataType(DataTypeManager dtm) { - super("uint16", false, dtm); + super("uint16", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java index e9ac687917..eb1ee6c391 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger3DataType.java @@ -17,9 +17,7 @@ package ghidra.program.model.data; import ghidra.util.classfinder.ClassTranslator; -public class UnsignedInteger3DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class UnsignedInteger3DataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedInteger3DataType instance.*/ public final static UnsignedInteger3DataType dataType = new UnsignedInteger3DataType(); @@ -34,7 +32,7 @@ public class UnsignedInteger3DataType extends AbstractIntegerDataType { } public UnsignedInteger3DataType(DataTypeManager dtm) { - super("uint3", false, dtm); + super("uint3", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java index 6acdeb20e9..c0001cc35d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger5DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class UnsignedInteger5DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class UnsignedInteger5DataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedInteger5DataType instance.*/ public final static UnsignedInteger5DataType dataType = new UnsignedInteger5DataType(); @@ -27,7 +25,7 @@ public class UnsignedInteger5DataType extends AbstractIntegerDataType { } public UnsignedInteger5DataType(DataTypeManager dtm) { - super("uint5", false, dtm); + super("uint5", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java index e98562d40f..e952b0e144 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger6DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class UnsignedInteger6DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class UnsignedInteger6DataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedInteger6DataType instance.*/ public final static UnsignedInteger6DataType dataType = new UnsignedInteger6DataType(); @@ -27,7 +25,7 @@ public class UnsignedInteger6DataType extends AbstractIntegerDataType { } public UnsignedInteger6DataType(DataTypeManager dtm) { - super("uint6", false, dtm); + super("uint6", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java index 381c790f14..b69b0d2123 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedInteger7DataType.java @@ -15,9 +15,7 @@ */ package ghidra.program.model.data; -public class UnsignedInteger7DataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class UnsignedInteger7DataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedInteger7DataType instance.*/ public final static UnsignedInteger7DataType dataType = new UnsignedInteger7DataType(); @@ -27,7 +25,7 @@ public class UnsignedInteger7DataType extends AbstractIntegerDataType { } public UnsignedInteger7DataType(DataTypeManager dtm) { - super("uint7", false, dtm); + super("uint7", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java index 76ca393b36..e3445d53f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedIntegerDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for an unsigned Integer dataType */ -public class UnsignedIntegerDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class UnsignedIntegerDataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedIntegerDataType instance.*/ public final static UnsignedIntegerDataType dataType = new UnsignedIntegerDataType(); @@ -30,30 +28,19 @@ public class UnsignedIntegerDataType extends AbstractIntegerDataType { } public UnsignedIntegerDataType(DataTypeManager dtm) { - super("uint", false, dtm); + super("uint", dtm); } - /** - * - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getIntegerSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Unsigned Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java index 4fb6e2aa87..f4e662f2c9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for a Signed Long Integer dataType */ -public class UnsignedLongDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class UnsignedLongDataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedLongDataType instance.*/ public final static UnsignedLongDataType dataType = new UnsignedLongDataType(); @@ -30,30 +28,19 @@ public class UnsignedLongDataType extends AbstractIntegerDataType { } public UnsignedLongDataType(DataTypeManager dtm) { - super("ulong", false, dtm); + super("ulong", dtm); } - /** - * - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getLongSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Unsigned Long Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java index c46bb4a33c..b6f653ea90 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedLongLongDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for an Signed LongLong Integer dataType */ -public class UnsignedLongLongDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class UnsignedLongLongDataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedLongLongDataType instance.*/ public final static UnsignedLongLongDataType dataType = new UnsignedLongLongDataType(); @@ -30,30 +28,19 @@ public class UnsignedLongLongDataType extends AbstractIntegerDataType { } public UnsignedLongLongDataType(DataTypeManager dtm) { - super("ulonglong", false, dtm); + super("ulonglong", dtm); } - /** - * - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getLongLongSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Unsigned Long Long Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java index 57d40651f0..a1bfd44203 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnsignedShortDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Basic implementation for a Short Integer dataType */ -public class UnsignedShortDataType extends AbstractIntegerDataType { - - private final static long serialVersionUID = 1; +public class UnsignedShortDataType extends AbstractUnsignedIntegerDataType { /** A statically defined UnsignedShortDataType instance.*/ public final static UnsignedShortDataType dataType = new UnsignedShortDataType(); @@ -30,30 +28,19 @@ public class UnsignedShortDataType extends AbstractIntegerDataType { } public UnsignedShortDataType(DataTypeManager dtm) { - super("ushort", false, dtm); + super("ushort", dtm); } - /** - * - * @see ghidra.program.model.data.DataType#getLength() - */ @Override public int getLength() { return getDataOrganization().getShortSize(); } - /** - * @see ghidra.program.model.data.DataType#hasLanguageDependantLength() - */ @Override public boolean hasLanguageDependantLength() { return true; } - /** - * - * @see ghidra.program.model.data.DataType#getDescription() - */ @Override public String getDescription() { return "Unsigned Short Integer (compiler-specific size)"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java index f33ed19a70..9ffea46759 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/WordDataType.java @@ -18,9 +18,7 @@ package ghidra.program.model.data; /** * Provides a basic implementation of a word datatype */ -public class WordDataType extends AbstractIntegerDataType { - - private static final long serialVersionUID = 1L; +public class WordDataType extends AbstractUnsignedIntegerDataType { /** A statically defined WordDataType instance.*/ public final static WordDataType dataType = new WordDataType(); @@ -30,7 +28,7 @@ public class WordDataType extends AbstractIntegerDataType { } public WordDataType(DataTypeManager dtm) { - super("word", false, dtm); + super("word", dtm); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java index 78487bfba8..09e34cc9b4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java @@ -32,9 +32,9 @@ import generic.jar.ResourceFile; import generic.stl.Pair; import ghidra.app.plugin.processors.sleigh.*; import ghidra.program.model.address.*; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.DefaultProgramContext; -import ghidra.program.model.listing.Parameter; +import ghidra.program.model.data.DataOrganization; +import ghidra.program.model.data.DataOrganizationImpl; +import ghidra.program.model.listing.*; import ghidra.program.model.pcode.*; import ghidra.util.Msg; import ghidra.util.SystemUtilities; @@ -312,6 +312,12 @@ public class BasicCompilerSpec implements CompilerSpec { @Override public PrototypeModel getCallingConvention(String name) { + if (name == null || Function.UNKNOWN_CALLING_CONVENTION_STRING.equals(name)) { + return null; + } + if (Function.DEFAULT_CALLING_CONVENTION_STRING.equals(name)) { + return getDefaultCallingConvention(); + } return callingConventionMap.get(name); } @@ -1077,12 +1083,14 @@ public class BasicCompilerSpec implements CompilerSpec { } @Override - public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention) { - if (genericCallingConvention == GenericCallingConvention.unknown) { + public PrototypeModel matchConvention(String conventionName) { + if (conventionName == null || + CALLING_CONVENTION_unknown.equals(conventionName) || + CALLING_CONVENTION_default.equals(conventionName)) { return defaultModel; } for (PrototypeModel model : models) { - if (model.getGenericCallingConvention() == genericCallingConvention) { + if (model.getName().equals(conventionName)) { return model; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java index f208ee8c33..061f6057a4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpec.java @@ -21,7 +21,6 @@ import java.util.Set; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.DataOrganization; -import ghidra.program.model.data.GenericCallingConvention; import ghidra.program.model.listing.DefaultProgramContext; import ghidra.program.model.listing.Parameter; import ghidra.program.model.pcode.Encoder; @@ -40,6 +39,9 @@ import ghidra.program.model.pcode.Encoder; */ public interface CompilerSpec { + public static final String CALLING_CONVENTION_unknown = "unknown"; + public static final String CALLING_CONVENTION_default = "default"; + public final static String CALLING_CONVENTION_cdecl = "__cdecl"; public final static String CALLING_CONVENTION_pascal = "__pascal"; public final static String CALLING_CONVENTION_thiscall = "__thiscall"; @@ -174,15 +176,16 @@ public interface CompilerSpec { public PcodeInjectLibrary getPcodeInjectLibrary(); /** - * Get the PrototypeModel corresponding to the given generic calling convention - * @param genericCallingConvention is the given generic calling convention + * Get the PrototypeModel which corresponds to the given calling convention name. + * If no match is found the default prototype model is returned. + * @param conventionName calling convention name. * @return the matching model or the defaultModel if nothing matches */ - public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention); + public PrototypeModel matchConvention(String conventionName); /** * Find the best guess at a calling convention model from this compiler spec - * given an ordered list of (potential) parameters. + * given an ordered list of (potential) parameters with storage assignments. * @param params is the ordered list of parameters * @return prototype model corresponding to the specified function signature */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpecID.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpecID.java index 421c6f9d81..53b1b21202 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpecID.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/CompilerSpecID.java @@ -18,17 +18,20 @@ package ghidra.program.model.lang; /** * Represents an opinion's compiler (gcc, borlandcpp, etc). */ -public class CompilerSpecID implements Comparable { +public final class CompilerSpecID implements Comparable { + + public static final String DEFAULT_ID = "default"; private final String id; /** * Creates a new compiler spec ID. * - * @param id The compiler ID (gcc, borlandcpp, etc). + * @param id The compiler ID (gcc, borlandcpp, etc) as defined in the appropriate + * {@link LanguageDescription}. If null the value of "default" will be assumed. */ public CompilerSpecID(String id) { - this.id = id; + this.id = id != null ? id : DEFAULT_ID; } /** @@ -38,12 +41,6 @@ public class CompilerSpecID implements Comparable { * @throws IllegalArgumentException if the compiler spec ID is null or empty. */ public String getIdAsString() { - if (id == null) { - throw new IllegalArgumentException("id == null not allowed"); - } - if ("".equals(id)) { - throw new IllegalArgumentException("empty id not allowed"); - } return id; } @@ -51,7 +48,7 @@ public class CompilerSpecID implements Comparable { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + id.hashCode(); return result; } @@ -67,15 +64,7 @@ public class CompilerSpecID implements Comparable { return false; } final CompilerSpecID other = (CompilerSpecID) obj; - if (id == null) { - if (other.id != null) { - return false; - } - } - else if (!id.equals(other.id)) { - return false; - } - return true; + return id.equals(other.id); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/DynamicVariableStorage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/DynamicVariableStorage.java index a74ad79b44..2496c4b6c6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/DynamicVariableStorage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/DynamicVariableStorage.java @@ -16,7 +16,8 @@ package ghidra.program.model.lang; import ghidra.program.model.address.Address; -import ghidra.program.model.listing.*; +import ghidra.program.model.listing.AutoParameterType; +import ghidra.program.model.listing.VariableStorage; import ghidra.program.model.pcode.Varnode; import ghidra.util.exception.InvalidInputException; @@ -56,7 +57,8 @@ public class DynamicVariableStorage extends VariableStorage { * @param size varnode size * @throws InvalidInputException */ - public DynamicVariableStorage(Program program, AutoParameterType autoParamType, Address address, + public DynamicVariableStorage(ProgramArchitecture program, AutoParameterType autoParamType, + Address address, int size) throws InvalidInputException { super(program, address, size); this.autoParamType = autoParamType; @@ -69,7 +71,8 @@ public class DynamicVariableStorage extends VariableStorage { * @param varnodes one or more ordered storage varnodes * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public DynamicVariableStorage(Program program, AutoParameterType autoParamType, Varnode... varnodes) + public DynamicVariableStorage(ProgramArchitecture program, AutoParameterType autoParamType, + Varnode... varnodes) throws InvalidInputException { super(program, varnodes); this.autoParamType = autoParamType; @@ -84,7 +87,8 @@ public class DynamicVariableStorage extends VariableStorage { * @param size varnode size * @throws InvalidInputException */ - public DynamicVariableStorage(Program program, boolean forcedIndirect, Address address, int size) + public DynamicVariableStorage(ProgramArchitecture program, boolean forcedIndirect, + Address address, int size) throws InvalidInputException { super(program, address, size); this.forcedIndirect = forcedIndirect; @@ -98,7 +102,8 @@ public class DynamicVariableStorage extends VariableStorage { * @param varnodes one or more ordered storage varnodes * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public DynamicVariableStorage(Program program, boolean forcedIndirect, Varnode... varnodes) + public DynamicVariableStorage(ProgramArchitecture program, boolean forcedIndirect, + Varnode... varnodes) throws InvalidInputException { super(program, varnodes); this.forcedIndirect = forcedIndirect; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageCompilerSpecPair.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageCompilerSpecPair.java index 408a60c535..b561b3cb26 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageCompilerSpecPair.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/LanguageCompilerSpecPair.java @@ -70,6 +70,22 @@ public final class LanguageCompilerSpecPair implements Comparable languageMinorVersion) { // Minor version change - translator not needed (languageUpgradeTranslator is null) - return new LanguageVersionException(); + String fromVer = languageVersion + "." + languageMinorVersion; + String toVer = languageVersion + "." + language.getMinorVersion(); + return new LanguageVersionException("Minor language change " + fromVer + " -> " + toVer, + true); } else if (language.getMinorVersion() != languageMinorVersion || language.getVersion() != languageVersion) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java new file mode 100644 index 0000000000..e38ebb7139 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ProgramArchitecture.java @@ -0,0 +1,57 @@ +/* ### + * 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.model.lang; + +import ghidra.program.model.address.AddressFactory; +import ghidra.program.model.address.OverlayAddressSpace; +import ghidra.program.model.listing.Program; + +/** + * ProgramArchitecture which identifies program architecture details required to + * utilize language/compiler-specific memory and variable storage specifications. + */ +public interface ProgramArchitecture { + + /** + * Get the processor language + * @return processor language + */ + Language getLanguage(); + + /** + * Get the address factory for this architecture. In the case of a {@link Program} this should + * be the extended address factory that includes the stack space and any defined overlay + * spaces (i.e., {@link OverlayAddressSpace}). + * @return address factory + */ + AddressFactory getAddressFactory(); + + /** + * Get the compiler specification + * @return compiler specification + */ + CompilerSpec getCompilerSpec(); + + /** + * Get the language/compiler spec ID pair associated with this program architecture. + * @return language/compiler spec ID pair + */ + public default LanguageCompilerSpecPair getLanguageCompilerSpecPair() { + return new LanguageCompilerSpecPair(getLanguage().getLanguageID(), + getCompilerSpec().getCompilerSpecID()); + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java index 52a7dd2c34..b67b21b472 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModel.java @@ -55,7 +55,6 @@ public class PrototypeModel { private AddressSet localRange; // Range on the stack considered for local storage private AddressSet paramRange; // Range on the stack considered for parameter storage private InputListType inputListType = InputListType.STANDARD; - private GenericCallingConvention genericCallingConvention; private boolean hasThis; // Convention has a this (auto-parameter) private boolean isConstruct; // Convention is used for object construction private boolean hasUponEntry; // Does this have an uponentry injection @@ -88,7 +87,6 @@ public class PrototypeModel { paramRange = new AddressSet(model.paramRange); hasThis = model.hasThis || name.equals(CompilerSpec.CALLING_CONVENTION_thiscall); isConstruct = model.isConstruct; - genericCallingConvention = GenericCallingConvention.getGenericCallingConvention(name); hasUponEntry = model.hasUponEntry; hasUponReturn = model.hasUponReturn; } @@ -107,21 +105,12 @@ public class PrototypeModel { compatModel = null; localRange = null; paramRange = null; - genericCallingConvention = GenericCallingConvention.unknown; hasThis = false; isConstruct = false; hasUponEntry = false; hasUponReturn = false; } - /** - * Get the generic calling convention enum associated with this - * @return the enum - */ - public GenericCallingConvention getGenericCallingConvention() { - return genericCallingConvention; - } - /** * @return list of registers unaffected by called functions */ @@ -439,10 +428,6 @@ public class PrototypeModel { encoder.writeString(ATTRIB_EXTRAPOP, "unknown"); } encoder.writeSignedInteger(ATTRIB_STACKSHIFT, stackshift); - GenericCallingConvention nameType = GenericCallingConvention.guessFromName(name); - if (nameType != genericCallingConvention) { - encoder.writeString(ATTRIB_TYPE, genericCallingConvention.getDeclarationName()); - } if (hasThis) { encoder.writeBool(ATTRIB_HASTHIS, true); } @@ -602,13 +587,6 @@ public class PrototypeModel { extrapop = SpecXmlUtils.decodeInt(extpopStr); } stackshift = SpecXmlUtils.decodeInt(protoElement.getAttribute("stackshift")); - String type = protoElement.getAttribute("type"); - if (type != null) { - genericCallingConvention = GenericCallingConvention.getGenericCallingConvention(type); - } - else { - genericCallingConvention = GenericCallingConvention.guessFromName(name); - } hasThis = false; isConstruct = false; String thisString = protoElement.getAttribute("hasthis"); @@ -742,9 +720,6 @@ public class PrototypeModel { if (extrapop != obj.extrapop || stackshift != obj.stackshift) { return false; } - if (genericCallingConvention != obj.genericCallingConvention) { - return false; - } if (hasThis != obj.hasThis || isConstruct != obj.isConstruct) { return false; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModelMerged.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModelMerged.java index a8f48cccf8..cae228ceda 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModelMerged.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/PrototypeModelMerged.java @@ -24,6 +24,7 @@ import java.util.*; import ghidra.app.plugin.processors.sleigh.SleighException; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Parameter; +import ghidra.program.model.listing.VariableStorage; import ghidra.program.model.pcode.Encoder; import ghidra.xml.*; @@ -101,7 +102,11 @@ public class PrototypeModelMerged extends PrototypeModel { for (int i = 0; i < modellist.length; ++i) { ScoreProtoModel scoremodel = new ScoreProtoModel(true, modellist[i], params.length); for (Parameter p : params) { - scoremodel.addParameter(p.getMinAddress(), p.getLength()); + VariableStorage storage = p.getVariableStorage(); + if (storage.isUnassignedStorage() || storage.isBadStorage()) { + continue; + } + scoremodel.addParameter(storage.getMinAddress(), p.getLength()); } scoremodel.doScore(); int score = scoremodel.getScore(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java index 96b8d793bd..9de3080709 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataTypeArchive.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +15,10 @@ */ package ghidra.program.model.listing; -import ghidra.program.model.data.DataTypeManagerDomainObject; - import java.util.Date; +import ghidra.program.model.data.DataTypeManagerDomainObject; + /** * This interface represents the main entry point into an object which * stores all information relating to a single data type archive. @@ -37,6 +36,13 @@ public interface DataTypeArchive extends DataTypeManagerDomainObject { /** A date from January 1, 1970 */ public static final Date JANUARY_1_1970 = new Date(0); + /** + * Determine if this archive has exclusive-write access which may be neccessary for some + * operations. + * @return true if archive has exclusive-write access + */ + public boolean hasExclusiveAccess(); + /** * Gets the default pointer size as it may be stored within the data type archive. * @return default pointer size. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java index eb3dbb09ad..a4437ef4c2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Function.java @@ -22,6 +22,8 @@ import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.symbol.*; import ghidra.util.exception.DuplicateNameException; @@ -42,8 +44,10 @@ public interface Function extends Namespace { public static final String DEFAULT_LOCAL_RESERVED_PREFIX = "local_res"; public static final String DEFAULT_LOCAL_TEMP_PREFIX = "temp_"; public static final int DEFAULT_LOCAL_PREFIX_LEN = DEFAULT_LOCAL_PREFIX.length(); - public static final String UNKNOWN_CALLING_CONVENTION_STRING = "unknown"; - public static final String DEFAULT_CALLING_CONVENTION_STRING = "default"; + public static final String UNKNOWN_CALLING_CONVENTION_STRING = + CompilerSpec.CALLING_CONVENTION_unknown; + public static final String DEFAULT_CALLING_CONVENTION_STRING = + CompilerSpec.CALLING_CONVENTION_default; public static final String INLINE = "inline"; public static final String NORETURN = "noreturn"; public static final String THUNK = "thunk"; @@ -177,7 +181,7 @@ public interface Function extends Namespace { /** * Set the function's return type. * @param type the dataType that will define this functions return type. - * @param source TODO + * @param source signature source * @throws InvalidInputException if data type is not a fixed length. */ public void setReturnType(DataType type, SourceType source) throws InvalidInputException; @@ -399,11 +403,13 @@ public interface Function extends Namespace { Variable... params) throws DuplicateNameException, InvalidInputException; /** - * Replace all current parameters with the given list of parameters and optionally change the calling convention - * and function return. + * Replace all current parameters with the given list of parameters and optionally change the + * calling convention and function return. * The {@link VariableUtilities#checkVariableConflict(Function, Variable, VariableStorage, boolean)} * method may be used to check and remove conflicting variables which already exist in the function. - * @param callingConvention updated calling convention name or null if no change is required + * @param callingConvention updated calling convention name or null if no change is required. + * Only {@link DataTypeManager#getKnownCallingConventionNames() known calling convention names} + * may be specified which will always include those defined by the associated {@link CompilerSpec}. * @param returnValue return variable or null if no change required * @param updateType function update type * @param force if true any conflicting local parameters will be removed @@ -422,11 +428,13 @@ public interface Function extends Namespace { throws DuplicateNameException, InvalidInputException; /** - * Replace all current parameters with the given list of parameters and optionally change the calling convention - * and function return. + * Replace all current parameters with the given list of parameters and optionally change the + * calling convention and function return. * The {@link VariableUtilities#checkVariableConflict(Function, Variable, VariableStorage, boolean)} * method may be used to check and remove conflicting variables which already exist in the function. - * @param callingConvention updated calling convention name or null if no change is required + * @param callingConvention updated calling convention name or null if no change is required. + * Only {@link DataTypeManager#getKnownCallingConventionNames() known calling convention names} + * may be specified which will always include those defined by the associated {@link CompilerSpec}. * @param returnVar return variable or null if no change required * @param updateType function update type * @param force if true any conflicting local parameters will be removed @@ -614,6 +622,14 @@ public interface Function extends Namespace { */ public PrototypeModel getCallingConvention(); + /** + * Determine if this signature has an unknown or unrecognized calling convention name. + * @return true if calling convention is unknown or unrecognized name, else false. + */ + public default boolean hasUnknownCallingConventionName() { + return getCallingConvention() == null; + } + /** * Gets the calling convention's name for this function. * @@ -626,22 +642,19 @@ public interface Function extends Namespace { public String getCallingConventionName(); /** - * Gets the name of the default calling convention. - *
Note: The name in the PrototypeModel of the default calling convention may be null. - * - * @return the name of the default calling convention. - */ - public String getDefaultCallingConventionName(); - - /** - * Sets the calling convention for this function to the named calling convention. - * @param name the name of the calling convention. "unknown" and "default" are reserved names - * that can also be used here. - *
Null or Function.UNKNOWN_CALLING_CONVENTION_STRING sets this function to not have a - * calling convention (i.e. unknown). - *
Function.DEFAULT_CALLING_CONVENTION_STRING sets this function to use the default calling - * convention. (i.e. default) - * @throws InvalidInputException if the specified name is not a recognized calling convention name. + * Sets the calling convention for this function to the named calling convention. Only + * {@link DataTypeManager#getKnownCallingConventionNames() known calling convention names} + * may be specified which will always include those defined by the associated + * {@link CompilerSpec}. + * @param name the name of the calling convention. Only + * {@link DataTypeManager#getKnownCallingConventionNames() known calling convention names} + * may be specified which will always include those defined by the associated + * {@link CompilerSpec}. In addition the reserved names + * {@link Function#UNKNOWN_CALLING_CONVENTION_STRING "unknown"} and + * {@link Function#DEFAULT_CALLING_CONVENTION_STRING "default"} may also be + * used here. + * @throws InvalidInputException if the specified name is not a recognized calling + * convention name. */ public void setCallingConvention(String name) throws InvalidInputException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java index be598d8661..5194a53efe 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionManager.java @@ -15,13 +15,14 @@ */ package ghidra.program.model.listing; +import java.util.Collection; import java.util.Iterator; -import java.util.List; import ghidra.program.database.ManagerDB; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.SourceType; @@ -41,13 +42,18 @@ public interface FunctionManager extends ManagerDB { public Program getProgram(); /** - * Gets the names associated with each of the current calling conventions associated with this - * program. Within the exception of "unknown", all of these calling convention names should have - * a PrototypeModel. + * Get the ordered list of defined calling convention names. The reserved names + * "unknown" and "default" are not included. The returned collection may not include all names + * referenced by various functions and function-definitions. This set is limited to those + * defined by the associated compiler specification. + * See {@link DataTypeManager#getDefinedCallingConventionNames}. + *

+ * For a set of all known names (including those that are not defined by compiler spec) + * see {@link DataTypeManager#getKnownCallingConventionNames()}. * * @return the calling convention names. */ - public List getCallingConventionNames(); + public Collection getCallingConventionNames(); /** * Gets the default calling convention's prototype model in this program. @@ -63,13 +69,6 @@ public interface FunctionManager extends ManagerDB { */ public PrototypeModel getCallingConvention(String name); - /** - * Gets all the calling convention prototype models in this program that have names. - * - * @return the function calling convention prototype models. - */ - public PrototypeModel[] getCallingConventions(); - /** * Create a function with the given body at entry point within the global namespace. * diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionSignature.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionSignature.java index cef0ffe7ce..8ae21a15db 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionSignature.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/FunctionSignature.java @@ -15,7 +15,9 @@ */ package ghidra.program.model.listing; -import ghidra.program.model.data.*; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.ParameterDefinition; +import ghidra.program.model.lang.PrototypeModel; /** * Interface describing all the things about a function that are portable @@ -23,6 +25,7 @@ import ghidra.program.model.data.*; */ public interface FunctionSignature { + public static final String NORETURN_DISPLAY_STRING = "noreturn"; public static final String VAR_ARGS_DISPLAY_STRING = "..."; public static final String VOID_PARAM_DISPLAY_STRING = "void"; @@ -32,43 +35,73 @@ public interface FunctionSignature { public String getName(); /** - * Return a string representation of the function signature without the + * Get string representation of the function signature without the * calling convention specified. + * @return function signature string */ public String getPrototypeString(); /** - * Return a string representation of the function signature + * Get string representation of the function signature * @param includeCallingConvention if true prototype will include call convention - * declaration if known. + * declaration if known as well as noreturn indicator if applicable. + * @return function signature string */ public String getPrototypeString(boolean includeCallingConvention); /** - * Return an array of parameters for the function + * Get function signature parameter arguments + * @return an array of parameters for the function */ public ParameterDefinition[] getArguments(); /** - * Return the return data type + * Get function signature return type + * @return the return data type */ public DataType getReturnType(); /** - * Return the comment string + * Get descriptive comment for signature + * @return the comment string */ public String getComment(); /** - * Returns true if this function signature has a variable argument list (VarArgs). + * @return true if this function signature has a variable argument list (VarArgs). */ public boolean hasVarArgs(); /** - * Returns the generic calling convention associated with this function definition. - * The "unknown" convention should be returned instead of null. + * @return true if this function signature corresponds to a non-returning function. */ - public GenericCallingConvention getGenericCallingConvention(); + public boolean hasNoReturn(); + + /** + * Gets the calling convention prototype model for this function if associated with a + * compiler specificfation. This method will always return null if signature is not + * associated with a specific program architecture. + * + * @return the prototype model of the function's current calling convention or null. + */ + public PrototypeModel getCallingConvention(); + + /** + * Returns the calling convention name associated with this function definition. + * Reserved names may also be returned: {@link Function#UNKNOWN_CALLING_CONVENTION_STRING}, + * {@link Function#DEFAULT_CALLING_CONVENTION_STRING}. + * The "unknown" convention must be returned instead of null. + * @return calling convention name + */ + public String getCallingConventionName(); + + /** + * Determine if this signature has an unknown or unrecognized calling convention name. + * @return true if calling convention is unknown or unrecognized name, else false. + */ + public default boolean hasUnknownCallingConventionName() { + return getCallingConvention() == null; + } /** * Returns true if the given signature is equivalent to this signature. The diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java index 948d01fcbd..2a2f5984a2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java @@ -46,7 +46,7 @@ import ghidra.util.task.TaskMonitor; * For example, the createCodeUnit() method of listing will fail if memory is * undefined at the address where the codeUnit is to be created. */ -public interface Program extends DataTypeManagerDomainObject { +public interface Program extends DataTypeManagerDomainObject, ProgramArchitecture { public static final String ANALYSIS_PROPERTIES = "Analyzers"; public static final String DISASSEMBLER_PROPERTIES = "Disassembler"; @@ -83,8 +83,10 @@ public interface Program extends DataTypeManagerDomainObject { /** * Get the internal program address map * @return internal address map + * @deprecated Method intended for internal ProgramDB use and is not intended for general use. + * This method may be removed from this interface in a future release. */ - // FIXME!! Should not expose on interface - anything using this should use ProgramDB or avoid using map! + @Deprecated(forRemoval = true) public AddressMap getAddressMap(); /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java index db51390974..f314185b5a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java @@ -20,8 +20,7 @@ import java.util.List; import generic.algorithms.CRC64; import ghidra.program.model.address.*; -import ghidra.program.model.lang.Register; -import ghidra.program.model.lang.UnknownRegister; +import ghidra.program.model.lang.*; import ghidra.program.model.pcode.Varnode; import ghidra.program.util.LanguageTranslator; import ghidra.util.exception.InvalidInputException; @@ -58,7 +57,7 @@ public class VariableStorage implements Comparable { public static final VariableStorage VOID_STORAGE = new VariableStorage(); protected final Varnode[] varnodes; - protected final Program program; + protected final ProgramArchitecture programArch; private List registers; private int size; @@ -69,42 +68,45 @@ public class VariableStorage implements Comparable { * Construct an empty variable storage for reserved usage (i.e., BAD_STORAGE, UNMAPPED_STORAGE) */ protected VariableStorage() { - this.program = null; + this.programArch = null; this.varnodes = null; } /** * Construct variable storage - * @param program + * @param programArch program architecture details * @param varnodes one or more ordered storage varnodes * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public VariableStorage(Program program, Varnode... varnodes) throws InvalidInputException { - this.program = program; + public VariableStorage(ProgramArchitecture programArch, Varnode... varnodes) + throws InvalidInputException { + this.programArch = programArch; this.varnodes = varnodes.clone(); checkVarnodes(); } /** * Construct register variable storage - * @param program + * @param programArch program architecture details * @param registers one or more ordered registers * @throws InvalidInputException if specified registers violate storage restrictions */ - public VariableStorage(Program program, Register... registers) throws InvalidInputException { - this(program, getVarnodeList(registers)); + public VariableStorage(ProgramArchitecture programArch, Register... registers) + throws InvalidInputException { + this(programArch, getVarnodeList(registers)); } /** * Construct stack variable storage - * @param program + * @param programArch program architecture details * @param stackOffset stack offset * @param size stack element size * @throws InvalidInputException if specified registers violate storage restrictions */ - public VariableStorage(Program program, int stackOffset, int size) throws InvalidInputException { - this(program, new Varnode(program.getAddressFactory().getStackSpace().getAddress( - stackOffset), size)); + public VariableStorage(ProgramArchitecture programArch, int stackOffset, int size) + throws InvalidInputException { + this(programArch, new Varnode( + programArch.getAddressFactory().getStackSpace().getAddress(stackOffset), size)); } private static Varnode[] getVarnodeList(Register[] registers) { @@ -117,34 +119,37 @@ public class VariableStorage implements Comparable { /** * Construct variable storage - * @param program + * @param programArch program architecture details * @param varnodes one or more ordered storage varnodes * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public VariableStorage(Program program, List varnodes) throws InvalidInputException { - this.program = program; + public VariableStorage(ProgramArchitecture programArch, List varnodes) + throws InvalidInputException { + this.programArch = programArch; this.varnodes = varnodes.toArray(new Varnode[varnodes.size()]); checkVarnodes(); } /** * Construct variable storage - * @param program - * @param address - * @param size - * @throws InvalidInputException + * @param programArch program architecture details + * @param address varnode address + * @param size varnode size + * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public VariableStorage(Program program, Address address, int size) throws InvalidInputException { - this(program, new Varnode(address, size)); + public VariableStorage(ProgramArchitecture programArch, Address address, int size) + throws InvalidInputException { + this(programArch, new Varnode(address, size)); } /** * Construct variable storage - * @param program + * @param programArch program architecture details * @param serialization storage serialization string - * @throws InvalidInputException + * @return deserialized variable storage. {@link #BAD_STORAGE} may be returned on failure. + * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public static VariableStorage deserialize(Program program, String serialization) + public static VariableStorage deserialize(ProgramArchitecture programArch, String serialization) throws InvalidInputException { if (serialization == null || UNASSIGNED.equals(serialization)) { return UNASSIGNED_STORAGE; @@ -155,18 +160,18 @@ public class VariableStorage implements Comparable { if (BAD.equals(serialization)) { return BAD_STORAGE; } - List varnodes = getVarnodes(program.getAddressFactory(), serialization); + List varnodes = getVarnodes(programArch.getAddressFactory(), serialization); if (varnodes == null) { return BAD_STORAGE; } - return new VariableStorage(program, varnodes); + return new VariableStorage(programArch, varnodes); } /** * @return program for which this storage is associated */ - public Program getProgram() { - return program; + public ProgramArchitecture getProgramArchitecture() { + return programArch; } /** @@ -181,7 +186,7 @@ public class VariableStorage implements Comparable { throw new IllegalArgumentException("A minimum of one varnode must be specified"); } - AddressFactory addrFactory = program.getAddressFactory(); + AddressFactory addrFactory = programArch.getAddressFactory(); size = 0; for (int i = 0; i < varnodes.length; i++) { Varnode varnode = varnodes[i]; @@ -212,7 +217,8 @@ public class VariableStorage implements Comparable { } if (!storageAddr.isStackAddress()) { - Register reg = program.getRegister(storageAddr, varnode.getSize()); + Register reg = + programArch.getLanguage().getRegister(storageAddr, varnode.getSize()); if (reg != null && !(reg instanceof UnknownRegister)) { isRegister = true; if (registers == null) { @@ -255,12 +261,12 @@ public class VariableStorage implements Comparable { /** * Attempt to clone variable storage for use in a different program. * Dynamic storage characteristics will not be preserved. - * @param newProgram target program + * @param newProgramArch target program architecture details * @return cloned storage - * @throws InvalidInputException + * @throws InvalidInputException if specified varnodes violate storage restrictions */ - public VariableStorage clone(Program newProgram) throws InvalidInputException { - if (program == null || newProgram == program) { + public VariableStorage clone(ProgramArchitecture newProgramArch) throws InvalidInputException { + if (programArch == null || newProgramArch == programArch) { if (getClass().equals(VariableStorage.class)) { return this; // only reuse if simple VariableStorage instance } @@ -270,14 +276,14 @@ public class VariableStorage implements Comparable { if (isBadStorage()) { return BAD_STORAGE; } - return new VariableStorage(newProgram, varnodes); + return new VariableStorage(newProgramArch, varnodes); } - if (!newProgram.getLanguage().equals(program.getLanguage())) { + if (!newProgramArch.getLanguage().equals(programArch.getLanguage())) { throw new IllegalArgumentException( "Variable storage incompatible with program language: " + - newProgram.getLanguage().toString()); + newProgramArch.getLanguage().toString()); } - AddressFactory newAddressFactory = newProgram.getAddressFactory(); + AddressFactory newAddressFactory = newProgramArch.getAddressFactory(); Varnode[] v = getVarnodes(); Varnode[] newVarnodes = new Varnode[v.length]; for (int i = 0; i < v.length; i++) { @@ -291,7 +297,7 @@ public class VariableStorage implements Comparable { newVarnodes[i] = new Varnode(newSpace.getAddress(v[i].getOffset()), v[i].getSize()); } - return new VariableStorage(newProgram, newVarnodes); + return new VariableStorage(newProgramArch, newVarnodes); } @Override @@ -322,9 +328,9 @@ public class VariableStorage implements Comparable { builder.append(varnode.getSize()); } - private String getAddressString(Address address, int size) { + private String getAddressString(Address address, int sz) { if (address.isRegisterAddress() || address.isMemoryAddress()) { - Register register = program.getRegister(address, size); + Register register = programArch.getLanguage().getRegister(address, sz); if (register != null) { return register.toString(); } @@ -610,7 +616,7 @@ public class VariableStorage implements Comparable { /** * Determine if this variable storage intersects the specified variable storage - * @param variableStorage + * @param variableStorage other variable storage * @return true if any intersection exists between this storage and the specified * variable storage */ @@ -666,8 +672,8 @@ public class VariableStorage implements Comparable { /** * Determine if the specified address is contained within this storage - * @param address - * @return + * @param address address + * @return true if this storage varnode(s) contain specified address */ public boolean contains(Address address) { if (varnodes == null) { @@ -751,7 +757,7 @@ public class VariableStorage implements Comparable { /** * Generate VariableStorage serialization string - * @param varnodes + * @param varnodes one or more storage varnodes * @return storage serialization string useful for subsequent reconstruction * of a VariableStorage object */ @@ -773,9 +779,10 @@ public class VariableStorage implements Comparable { /** * Parse a storage serialization string to produce an array or varnodes - * @param addrFactory - * @param serialization + * @param addrFactory address factory + * @param serialization serialized variable storage string (see {@link #getSerializationString()}). * @return array of varnodes or null if invalid + * @throws InvalidInputException if specified registers violate storage restrictions */ public static List getVarnodes(AddressFactory addrFactory, String serialization) throws InvalidInputException { @@ -866,7 +873,7 @@ public class VariableStorage implements Comparable { translator.getOldLanguage().getAddressFactory().getRegisterSpace().getAddress( offset); String newOffsetStr = - translateRegisterVarnodeOffset(oldRegAddr, size, translator, space); + translateRegisterVarnodeOffset(oldRegAddr, size, translator); if (newOffsetStr != null) { // if mapping failed - leave it unchanged offsetStr = newOffsetStr; @@ -896,15 +903,15 @@ public class VariableStorage implements Comparable { } /** - * Translate register varnode address offsetStr - * @param translator - * @param space - * @param offsetStr - * @param sizeStr - * @return translated offsetStr or null if BAD translation + * Translate old register storage defined by oldRegAddr and varnodeSize using the + * specified translator. + * @param oldRegAddr old register address + * @param varnodeSize varnode size + * @param translator language translator + * @return new register offset within same space as oldRegAddr or null if translation failed. */ private static String translateRegisterVarnodeOffset(Address oldRegAddr, int varnodeSize, - LanguageTranslator translator, AddressSpace newRegisterSpace) { + LanguageTranslator translator) { // Handle register movement within register space only // Assumes all translators will map register space properly // If old or new register not found no adjustment is made diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java index 066e7871fd..9196b23385 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableUtilities.java @@ -243,7 +243,7 @@ public class VariableUtilities { } if (dtLen < storageSize && storage.isRegisterStorage()) { // TODO: this could be expanded to handle other storage - return new VariableStorage(storage.getProgram(), + return new VariableStorage(storage.getProgramArchitecture(), shrinkRegister(storage.getRegister(), storageSize - dtLen)); } throw new InvalidInputException( @@ -721,7 +721,7 @@ public class VariableUtilities { @Deprecated public static ParameterImpl getThisParameter(Function function, PrototypeModel convention) { if (convention != null && - convention.getGenericCallingConvention() == GenericCallingConvention.thiscall) { + CompilerSpec.CALLING_CONVENTION_thiscall.equals(convention.getName())) { DataType dt = findOrCreateClassStruct(function); if (dt == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java index 64393e2dd4..31b80daffb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java @@ -36,7 +36,6 @@ public class FunctionPrototype { private LocalSymbolMap localsyms; // Prototype backed by symbol map private String modelname; // Name of prototype model - private GenericCallingConvention gconv; // Generic name for the model private String injectname; // Name of pcode inject associated with this prototype private DataType returntype; // Output parameter private VariableStorage returnstorage; // Where the output value is stored @@ -64,7 +63,6 @@ public class FunctionPrototype { public FunctionPrototype(LocalSymbolMap ls, Function func) { localsyms = ls; modelname = null; - gconv = null; injectname = null; returntype = null; returnstorage = null; @@ -90,10 +88,9 @@ public class FunctionPrototype { */ public FunctionPrototype(FunctionSignature proto, CompilerSpec cspec, boolean voidimpliesdotdotdot) { - modelname = proto.getGenericCallingConvention().getDeclarationName(); - PrototypeModel model = cspec.matchConvention(proto.getGenericCallingConvention()); + modelname = proto.getName(); + PrototypeModel model = cspec.matchConvention(modelname); localsyms = null; - gconv = proto.getGenericCallingConvention(); injectname = null; returntype = proto.getReturnType(); returnstorage = null; @@ -103,7 +100,7 @@ public class FunctionPrototype { outputlock = true; dotdotdot = proto.hasVarArgs(); isinline = false; - noreturn = false; + noreturn = proto.hasNoReturn(); custom = false; extrapop = model.getExtrapop(); hasThis = model.hasThisPointer(); @@ -135,8 +132,7 @@ public class FunctionPrototype { protoModel = f.getProgram().getCompilerSpec().getDefaultCallingConvention(); } hasThis = protoModel.hasThisPointer(); - modellock = - ((modelname != null) && (modelname != Function.UNKNOWN_CALLING_CONVENTION_STRING)); + modellock = !f.hasUnknownCallingConventionName(); injectname = f.getCallFixup(); voidinputlock = false; Parameter returnparam = f.getReturn(); @@ -310,13 +306,6 @@ public class FunctionPrototype { return modelname; } - /** - * @return generic calling convention - */ - public GenericCallingConvention getGenericCallingConvention() { - return gconv; - } - /** * Encode this function prototype to a stream. * @param encoder is the stream encoder diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java index b7145c7b09..c85958659e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java @@ -696,10 +696,11 @@ public class HighFunctionDBUtil { ParameterDefinition[] params = sig.getArguments(); FunctionDefinitionDataType fsig = new FunctionDefinitionDataType("tmpname"); // Empty datatype, will get renamed later - fsig.setGenericCallingConvention(sig.getGenericCallingConvention()); + fsig.setCallingConvention(sig.getCallingConventionName()); fsig.setArguments(params); fsig.setReturnType(sig.getReturnType()); fsig.setVarArgs(sig.hasVarArgs()); + fsig.setNoReturn(sig.hasNoReturn()); DataTypeSymbol datsym = new DataTypeSymbol(fsig, "prt", AUTO_CAT); Program program = function.getProgram(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java index 94255200a2..904ffaf698 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighSymbol.java @@ -501,8 +501,8 @@ public class HighSymbol { AutoParameterType autoType = isThis ? AutoParameterType.THIS : AutoParameterType.RETURN_STORAGE_PTR; try { - VariableStorage newStorage = new DynamicVariableStorage(storage.getProgram(), - autoType, storage.getFirstVarnode()); + VariableStorage newStorage = new DynamicVariableStorage( + storage.getProgramArchitecture(), autoType, storage.getFirstVarnode()); entryList[0] = new MappedEntry(this, newStorage, entry.getPCAdress()); } catch (InvalidInputException e) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java index 0fa2f22c38..2e03b03a56 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslatorAdapter.java @@ -20,7 +20,6 @@ import java.util.*; import ghidra.program.model.address.*; import ghidra.program.model.data.DataOrganization; -import ghidra.program.model.data.GenericCallingConvention; import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.Encoder; @@ -55,7 +54,7 @@ public abstract class LanguageTranslatorAdapter implements LanguageTranslator { * @param oldLanguage * @param newLanguage */ - private LanguageTranslatorAdapter(Language oldLanguage, Language newLanguage) { + protected LanguageTranslatorAdapter(Language oldLanguage, Language newLanguage) { this.oldLanguage = oldLanguage; this.newLanguage = newLanguage; oldLanguageID = oldLanguage.getLanguageID(); @@ -483,7 +482,7 @@ public abstract class LanguageTranslatorAdapter implements LanguageTranslator { * @param newLanguage * @return default translator or null if reasonable mappings can not be determined. */ - static LanguageTranslator getDefaultLanguageTranslator(Language oldLanguage, + public static LanguageTranslator getDefaultLanguageTranslator(Language oldLanguage, Language newLanguage) { DefaultLanguageTranslator translator = @@ -626,7 +625,7 @@ class TemporaryCompilerSpec implements CompilerSpec { } @Override - public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention) { + public PrototypeModel matchConvention(String callingConvention) { throw new UnsupportedOperationException("Language for upgrade use only (matchConvention)"); } diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java index e861d8a285..5bedbfd670 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/EnumTest.java @@ -21,13 +21,13 @@ import java.util.NoSuchElementException; import org.junit.*; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; import ghidra.util.task.TaskMonitor; /** * Tests for Enum data types. */ -public class EnumTest extends AbstractGTest { +public class EnumTest extends AbstractGenericTest { private DataTypeManager dataMgr; diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java index f5adb2f71e..5db5c46761 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FileDataTypeManagerTest.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import org.junit.*; import generic.test.AbstractGenericTest; +import ghidra.program.model.data.StandAloneDataTypeManager.ArchiveWarning; public class FileDataTypeManagerTest extends AbstractGenericTest { @@ -68,6 +69,7 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { dtMgr = null; dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, false); + assertEquals(ArchiveWarning.NONE, dtMgr.getWarning()); assertFalse(dtMgr.isUpdatable()); ArrayList list = new ArrayList<>(); @@ -94,7 +96,7 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { dtMgr = null; } catch (IOException e) { - Assert.fail("Unexpected Exception"); + failWithException("Unexpected exception", e); } finally { if (dtMgr != null) { @@ -113,6 +115,7 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { FileDataTypeManager dtMgr = null; try { dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, true); + assertEquals(ArchiveWarning.NONE, dtMgr.getWarning()); assertTrue("Archive not updateable, i=" + i, dtMgr.isUpdatable()); int txId = dtMgr.startTransaction("Add Type"); @@ -143,6 +146,7 @@ public class FileDataTypeManagerTest extends AbstractGenericTest { try { dtMgr = FileDataTypeManager.openFileArchive(testArchiveFile, false); + assertEquals(ArchiveWarning.NONE, dtMgr.getWarning()); assertFalse(dtMgr.isUpdatable()); ArrayList list = new ArrayList<>(); diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java index 795d55324b..ee01baebde 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FunctionDefinitionDataTypeTest.java @@ -19,9 +19,9 @@ import static org.junit.Assert.*; import org.junit.*; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; -public class FunctionDefinitionDataTypeTest extends AbstractGTest { +public class FunctionDefinitionDataTypeTest extends AbstractGenericTest { private StandAloneDataTypeManager dtm; private FunctionDefinition functionDt; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java index c5d3f621ec..0aa7334fb6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/BitFieldDBDataTypeTest.java @@ -15,15 +15,15 @@ */ package ghidra.program.database.data; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; import ghidra.program.model.data.*; -public class BitFieldDBDataTypeTest extends AbstractGTest { +public class BitFieldDBDataTypeTest extends AbstractGenericTest { private DataTypeManagerDB dataMgr; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java index 2f8fc607d4..e9b0b581a7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java @@ -23,13 +23,13 @@ import org.junit.*; import com.google.common.collect.Sets; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; import ghidra.program.model.data.*; import ghidra.util.InvalidNameException; import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitorAdapter; -public class StructureDBTest extends AbstractGTest { +public class StructureDBTest extends AbstractGenericTest { private StructureDB struct; private DataTypeManagerDB dataMgr; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java index 6a0f1d6566..6152b93453 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java @@ -21,14 +21,14 @@ import org.junit.*; import com.google.common.collect.Sets; -import generic.test.AbstractGTest; +import generic.test.AbstractGenericTest; import ghidra.program.model.data.*; import ghidra.util.task.TaskMonitor; /** * */ -public class UnionDBTest extends AbstractGTest { +public class UnionDBTest extends AbstractGenericTest { private DataTypeManager dataMgr; private UnionDB union; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/FunctionTestDouble.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/FunctionTestDouble.java index e7b4344ea2..8f3f8094bc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/FunctionTestDouble.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/FunctionTestDouble.java @@ -383,12 +383,7 @@ public class FunctionTestDouble implements Function { @Override public String getCallingConventionName() { - throw new UnsupportedOperationException(); - } - - @Override - public String getDefaultCallingConventionName() { - throw new UnsupportedOperationException(); + return Function.UNKNOWN_CALLING_CONVENTION_STRING; } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java index 530aa56f84..d388eff5dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubFunctionManager.java @@ -53,11 +53,6 @@ public class StubFunctionManager implements FunctionManager { throw new UnsupportedOperationException(); } - @Override - public PrototypeModel[] getCallingConventions() { - throw new UnsupportedOperationException(); - } - @Override public Function createFunction(String name, Address entryPoint, AddressSetView body, SourceType source) throws InvalidInputException, OverlappingFunctionException { diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/TestDoubleFunctionSignature.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/TestDoubleFunctionSignature.java index 165b47a3b5..bac52f7e74 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/TestDoubleFunctionSignature.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/TestDoubleFunctionSignature.java @@ -15,7 +15,10 @@ */ package ghidra.program.model; -import ghidra.program.model.data.*; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.ParameterDefinition; +import ghidra.program.model.lang.PrototypeModel; +import ghidra.program.model.listing.Function; import ghidra.program.model.listing.FunctionSignature; /** @@ -65,10 +68,20 @@ public class TestDoubleFunctionSignature implements FunctionSignature { } @Override - public GenericCallingConvention getGenericCallingConvention() { + public boolean hasNoReturn() { throw new UnsupportedOperationException(); } + @Override + public PrototypeModel getCallingConvention() { + return null; + } + + @Override + public String getCallingConventionName() { + return Function.UNKNOWN_CALLING_CONVENTION_STRING; + } + @Override public String getPrototypeString() { return funtionSignature; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java index 643944123a..e6dedcd54c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java @@ -18,6 +18,9 @@ package ghidra.program.model.data; import java.util.*; import db.Transaction; +import ghidra.program.database.map.AddressMap; +import ghidra.program.model.lang.ProgramArchitecture; +import ghidra.program.model.lang.PrototypeModel; import ghidra.util.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -39,6 +42,26 @@ public class TestDoubleDataTypeManager implements DataTypeManager { return id; } + @Override + public AddressMap getAddressMap() { + throw new UnsupportedOperationException(); + } + + @Override + public ProgramArchitecture getProgramArchitecture() { + throw new UnsupportedOperationException(); + } + + @Override + public String getProgramArchitectureSummary() { + throw new UnsupportedOperationException(); + } + + @Override + public DataOrganization getDataOrganization() { + throw new UnsupportedOperationException(); + } + @Override public boolean containsCategory(CategoryPath path) { throw new UnsupportedOperationException(); @@ -85,6 +108,11 @@ public class TestDoubleDataTypeManager implements DataTypeManager { throw new UnsupportedOperationException(); } + @Override + public Iterator getAllFunctionDefinitions() { + throw new UnsupportedOperationException(); + } + @Override public void findDataTypes(String name, List list) { throw new UnsupportedOperationException(); @@ -322,11 +350,6 @@ public class TestDoubleDataTypeManager implements DataTypeManager { throw new UnsupportedOperationException(); } - @Override - public DataOrganization getDataOrganization() { - throw new UnsupportedOperationException(); - } - @Override public List getSourceArchives() { throw new UnsupportedOperationException(); @@ -356,4 +379,24 @@ public class TestDoubleDataTypeManager implements DataTypeManager { public boolean allowsDefaultComponentSettings() { return false; } + + @Override + public Collection getKnownCallingConventionNames() { + throw new UnsupportedOperationException(); + } + + @Override + public Collection getDefinedCallingConventionNames() { + throw new UnsupportedOperationException(); + } + + @Override + public PrototypeModel getDefaultCallingConvention() { + throw new UnsupportedOperationException(); + } + + @Override + public PrototypeModel getCallingConvention(String name) { + throw new UnsupportedOperationException(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java index d07ddef22e..b68306cc03 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java @@ -18,6 +18,9 @@ package ghidra.program.model.data; import java.util.*; import db.Transaction; +import ghidra.program.database.map.AddressMap; +import ghidra.program.model.lang.ProgramArchitecture; +import ghidra.program.model.lang.PrototypeModel; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.CancelledException; @@ -88,6 +91,12 @@ public class TestDummyDataTypeManager implements DataTypeManager { return null; } + @Override + public Iterator getAllFunctionDefinitions() { + // stub + return null; + } + @Override public void findDataTypes(String name, List list) { // stub @@ -371,6 +380,23 @@ public class TestDummyDataTypeManager implements DataTypeManager { return false; } + @Override + public AddressMap getAddressMap() { + // stub + return null; + } + + @Override + public ProgramArchitecture getProgramArchitecture() { + // stub + return null; + } + + @Override + public String getProgramArchitectureSummary() { + return null; + } + @Override public DataOrganization getDataOrganization() { // stub @@ -413,4 +439,24 @@ public class TestDummyDataTypeManager implements DataTypeManager { return false; } + @Override + public Collection getKnownCallingConventionNames() { + throw new UnsupportedOperationException(); + } + + @Override + public Collection getDefinedCallingConventionNames() { + throw new UnsupportedOperationException(); + } + + @Override + public PrototypeModel getDefaultCallingConvention() { + throw new UnsupportedOperationException(); + } + + @Override + public PrototypeModel getCallingConvention(String name) { + throw new UnsupportedOperationException(); + } + } diff --git a/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs b/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs index 421644f47e..a33d0cac6a 100644 --- a/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs +++ b/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs @@ -9,7 +9,7 @@ processorspec="AARCH64.pspec" manualindexfile="../manuals/AARCH64.idx" id="AARCH64:LE:64:v8A"> - Generic ARM v8.5-A LE instructions, LE data, missing some 8.5 vector + Generic ARM64 v8.5-A LE instructions, LE data, missing some 8.5 vector @@ -25,7 +25,7 @@ processorspec="AARCH64.pspec" manualindexfile="../manuals/AARCH64.idx" id="AARCH64:BE:64:v8A"> - Generic ARM v8.5-A LE instructions, BE data, missing some 8.5 vector + Generic ARM64 v8.5-A LE instructions, BE data, missing some 8.5 vector @@ -39,7 +39,7 @@ processorspec="AARCH64.pspec" manualindexfile="../manuals/AARCH64.idx" id="AARCH64:LE:32:ilp32"> - Generic ARM v8.5-A LE instructions, LE data, ilp32 + Generic ARM64 v8.5-A LE instructions, LE data, ilp32 @@ -55,7 +55,7 @@ processorspec="AARCH64.pspec" manualindexfile="../manuals/AARCH64.idx" id="AARCH64:BE:32:ilp32"> - Generic ARM v8.5-A LE instructions, BE data, ilp32 + Generic ARM64 v8.5-A LE instructions, BE data, ilp32 diff --git a/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java b/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java index f7480d2538..942527c795 100644 --- a/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java +++ b/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java @@ -23,10 +23,12 @@ import ghidra.file.formats.android.dex.format.*; import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.ConstantPool; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.Symbol; +import ghidra.util.exception.InvalidInputException; /** * Map Ghidra's generic ConstantPool interface onto the Dex specific constant pool @@ -121,8 +123,14 @@ public class ConstantPoolDex extends ConstantPool { String defName = res.token + '_' + Integer.toHexString(methodID); FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(defName, dtManager); res.type = new PointerDataType(funcDef); - funcDef.setGenericCallingConvention( - isStatic ? GenericCallingConvention.stdcall : GenericCallingConvention.thiscall); + try { + funcDef.setCallingConvention( + isStatic ? CompilerSpec.CALLING_CONVENTION_stdcall + : CompilerSpec.CALLING_CONVENTION_thiscall); + } + catch (InvalidInputException e) { + // unexpected + } int prototypeIndex = methodIDItem.getProtoIndex() & 0xffff; PrototypesIDItem prototype = dexHeader.getPrototypes().get(prototypeIndex); diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java index 9ec1451e6b..4e06fb49ee 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java @@ -21,8 +21,11 @@ import java.util.List; import ghidra.javaclass.format.*; import ghidra.javaclass.format.constantpool.*; import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.ConstantPool; import ghidra.program.model.listing.Program; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.InvalidInputException; public class ConstantPoolJava extends ConstantPool { @@ -103,20 +106,30 @@ public class ConstantPoolJava extends ConstantPool { new ParameterDefinitionImpl("", params.get(i), null); paramDefs[i] = currentParam; } - funcDef.setGenericCallingConvention(GenericCallingConvention.stdcall); + try { + funcDef.setCallingConvention(CompilerSpec.CALLING_CONVENTION_stdcall); + } + catch (InvalidInputException e) { + throw new AssertException(e); // unexpected + } } //invokeinterface, invokespecial, and invokevirtual do have a this pointer else { paramDefs = new ParameterDefinitionImpl[params.size() + 1]; ParameterDefinitionImpl thisParam = new ParameterDefinitionImpl("objectRef", - new Pointer32DataType(DataType.VOID), null); + new Pointer32DataType(VoidDataType.dataType), null); paramDefs[0] = thisParam; for (int i = 1, max = params.size(); i <= max; ++i) { ParameterDefinitionImpl currentParam = new ParameterDefinitionImpl("", params.get(i - 1), null); paramDefs[i] = currentParam; } - funcDef.setGenericCallingConvention(GenericCallingConvention.thiscall); + try { + funcDef.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); + } + catch (InvalidInputException e) { + throw new AssertException(e); // unexpected + } } funcDef.setArguments(paramDefs); res.type = new PointerDataType(funcDef); @@ -282,7 +295,7 @@ public class ConstantPoolJava extends ConstantPool { break; case CPOOL_MULTIANEWARRAY: res.tag = ConstantPool.CLASS_REFERENCE; - res.type = new PointerDataType(DataType.VOID); + res.type = new PointerDataType(VoidDataType.dataType); int nameIndex = ((ConstantPoolClassInfo) poolRef).getNameIndex(); ConstantPoolUtf8Info utf8Info = (ConstantPoolUtf8Info) constantPool[nameIndex]; String classNameWithSemicolon = utf8Info.getString(); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LanguageProviderPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LanguageProviderPluginScreenShots.java index f17d1c14c1..64af927171 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LanguageProviderPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/LanguageProviderPluginScreenShots.java @@ -37,7 +37,8 @@ public class LanguageProviderPluginScreenShots extends GhidraScreenShotGenerator @Test public void testLanguages() { - final SetLanguageDialog dialog = new SetLanguageDialog(tool, program); + final SetLanguageDialog dialog = new SetLanguageDialog(tool, program, + "Set Language: " + program.getDomainFile().getName()); Object newLanguagePanel = getInstanceField("selectLangPanel", dialog); final GTableFilterPanel filterPanel = (GTableFilterPanel) getInstanceField("tableFilterPanel", newLanguagePanel);