diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java index 455c2beebc..66289d9687 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgLaunchProcessCommand.java @@ -89,7 +89,9 @@ public class DbgLaunchProcessCommand extends AbstractDbgCommand { initialDirectory = fixPath(initialDirectory); environment = fixPath(environment); // NB: The intent here is to enable multi-line input via a single dialog field - environment = environment.replace("\\0", "\0"); + if (environment != null) { + environment = environment.replace("\\0", "\0"); + } dbgeng.createProcess(dbgeng.getLocalServer(), StringUtils.join(newArgs, " "), initialDirectory, environment, createFlags, engCreateFlags, verifierFlags); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPanel.java index 8fd6186556..4955351619 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPanel.java @@ -92,6 +92,9 @@ public class DebuggerModulesPanel extends AbstractObjectsTableBasedPanel attr = rowObject.getAttribute(TargetModule.RANGE_ATTRIBUTE_NAME, AddressRange.class); if (attr == null) { 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 c2bd9b7db7..9e52b95095 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 @@ -19,8 +19,8 @@ import java.io.IOException; import java.util.LinkedList; import java.util.concurrent.locks.ReadWriteLock; -import db.Transaction; import db.DBHandle; +import db.Transaction; import ghidra.framework.model.DomainFile; import ghidra.program.database.data.ProgramBasedDataTypeManagerDB; import ghidra.program.model.address.Address; @@ -70,6 +70,10 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB return trace.getBaseAddressFactory(); } }, null, false, monitor); + + if (openMode == DBOpenMode.CREATE) { + saveDataOrganization(); + } } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisManager.java index 5be6e12775..e83ea1bd0c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisManager.java @@ -1319,6 +1319,10 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl return; } + if (!p.isChanged()) { + return; // avoid storing task times if no other unsaved change exists + } + StoredAnalyzerTimes times = StoredAnalyzerTimes.getStoredAnalyzerTimes(program); String taskNames[] = getTimedTasks(); diff --git a/Ghidra/Framework/DB/src/main/java/db/util/ErrorHandler.java b/Ghidra/Framework/DB/src/main/java/db/util/ErrorHandler.java index bde2361366..bf1bbc816e 100644 --- a/Ghidra/Framework/DB/src/main/java/db/util/ErrorHandler.java +++ b/Ghidra/Framework/DB/src/main/java/db/util/ErrorHandler.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. @@ -25,6 +24,10 @@ public interface ErrorHandler { /** * Notification that an IO exception occurred. + * + * @param e {@link IOException} which was cause of error + * @throws RuntimeException optional exception which may be thrown when + * responding to error condition. */ - public void dbError(IOException e); + public void dbError(IOException e) throws RuntimeException; } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java new file mode 100644 index 0000000000..0293e14d6c --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/RuntimeIOException.java @@ -0,0 +1,33 @@ +/* ### + * 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.framework.model; + +import java.io.IOException; + +/** + * {@link RuntimeIOException} provide a wrapped {@link IOException} wrapped + * within a {@link RuntimeException}. + */ +public class RuntimeIOException extends RuntimeException { + + /** + * Construct {@link RuntimeIOException} + * @param e {@link IOException} instance + */ + public RuntimeIOException(IOException e) { + super(e); + } +} 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 4219895dec..ef4eed5a3c 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 @@ -1773,6 +1773,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM } listing.setProgram(this); + if (openMode == DBConstants.CREATE) { + getDataTypeManager().saveDataOrganization(); + } + monitor.checkCancelled(); if (openMode == UPGRADE) { 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 cadb6561ea..db16c2b1d4 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 @@ -31,6 +31,7 @@ import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; import ghidra.docking.settings.*; import ghidra.framework.Application; +import ghidra.framework.model.RuntimeIOException; import ghidra.framework.store.db.PackedDBHandle; import ghidra.framework.store.db.PackedDatabase; import ghidra.graph.*; @@ -123,10 +124,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private TreeSet knownCallingConventions; private TreeSet definedCallingConventions; - protected DBHandle dbHandle; - private int mode; // open mode (see DBConstants) + protected final boolean readOnlyMode; + protected final DBHandle dbHandle; protected final String tablePrefix; protected final ErrorHandler errHandler; + private DataTypeConflictHandler currentHandler; private CategoryDB root; @@ -209,8 +211,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * support the save or saveAs operation. No Language is associated with instance. * * @param dataOrganization applicable data organization + * @throws RuntimeIOException if database error occurs during creation */ - protected DataTypeManagerDB(DataOrganization dataOrganization) { + protected DataTypeManagerDB(DataOrganization dataOrganization) throws RuntimeIOException { this.lock = new Lock("DataTypeManagerDB"); this.errHandler = new DbErrorHandler(); this.dataOrganization = dataOrganization; @@ -218,8 +221,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { try { dbHandle = new DBHandle(); + readOnlyMode = false; int id = startTransaction(""); - try { init(DBConstants.CREATE, TaskMonitor.DUMMY); } @@ -231,7 +234,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } catch (IOException e) { - errHandler.dbError(e); + throw new RuntimeIOException(e); } } @@ -254,6 +257,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { this.errHandler = new DbErrorHandler(); this.lock = new Lock("DataTypeManagerDB"); this.tablePrefix = ""; + this.readOnlyMode = (openMode == DBConstants.READ_ONLY); File file = packedDBfile.getFile(false); if (file == null && openMode != DBConstants.READ_ONLY) { @@ -359,6 +363,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { throws CancelledException, IOException, VersionException { this.tablePrefix = tablePrefix != null ? tablePrefix : ""; this.dbHandle = handle; + this.readOnlyMode = (openMode == DBConstants.READ_ONLY); this.addrMap = addrMap; this.errHandler = errHandler; this.lock = lock; @@ -367,7 +372,6 @@ 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)) { @@ -795,6 +799,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @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. + * This should be false during create mode where only the state is affected without + * changing the Database or existing datatypes. * @param monitor task monitor * @throws IOException if IO error occurs * @throws CancelledException if processing cancelled - data types may not properly reflect @@ -812,10 +818,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { ? programArchitecture.getCompilerSpec().getDataOrganization() : DataOrganizationImpl.getDefaultOrganization(); - if (mode == DBConstants.CREATE) { - saveDataOrganization(); - } - else if (store) { + if (store) { try { compilerSpecChanged(monitor); updateLastChangeTime(); @@ -839,8 +842,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * updated compiler specification */ protected void compilerSpecChanged(TaskMonitor monitor) throws IOException, CancelledException { - - if (mode == DBConstants.READ_ONLY) { + + if (readOnlyMode) { throw new ReadOnlyException(); } @@ -861,6 +864,11 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return !Objects.equals(readDataOrganization(), getDataOrganization()); } + /** + * Save the current data organization to facilitate future change detection and + * upgrades. + * @throws IOException if failure occured while saving data organization. + */ protected void saveDataOrganization() throws IOException { DataOrganizationImpl.save(getDataOrganization(), getDataMap(true), "dataOrg."); } @@ -4620,14 +4628,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private class DbErrorHandler implements ErrorHandler { @Override - public void dbError(IOException e) { + public void dbError(IOException e) throws RuntimeIOException { String message = e.getMessage(); if (e instanceof ClosedException) { message = "Data type archive is closed: " + getName(); + Msg.showError(this, null, "IO ERROR", message, e); } - Msg.showError(this, null, "IO ERROR", message, e); + throw new RuntimeIOException(e); } } 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 f32994ef39..fedad0c640 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 @@ -64,6 +64,17 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB upgrade = (openMode == DBConstants.UPGRADE); } + /** + * Save the current data organization to facilitate future change detection and + * upgrades. This method must be invoked by {@link ProgramDB} during the final + * stage of program creation (i.e., openMode == CREATE). + * @throws IOException if failure occured while saving data organization. + */ + @Override + public void saveDataOrganization() throws IOException { + super.saveDataOrganization(); + } + @Override protected void dataSettingChanged(Address dataAddr) { program.setChanged(ChangeManager.DOCR_DATA_TYPE_SETTING_CHANGED, dataAddr, @@ -81,6 +92,7 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB try { setProgramArchitecture(p, p.getSymbolTable().getVariableStorageManager(), false, TaskMonitor.DUMMY); + // NOTE: Due to late manner in which program architecture is established, any // response to a data organization change must be handled during a language // upgrade and setLanguage 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 951d8bd8fd..e97a535531 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 @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList; import db.*; import db.util.ErrorHandler; import generic.jar.ResourceFile; +import ghidra.framework.model.RuntimeIOException; import ghidra.framework.store.LockException; import ghidra.program.database.DBStringMapAdapter; import ghidra.program.database.ProgramAddressFactory; @@ -138,8 +139,9 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos * Constructor for new temporary data-type manager using the default DataOrganization. * Note that this manager does not support the save or saveAs operation. * @param rootName Name of the root category. + * @throws RuntimeIOException if database error occurs during creation */ - public StandAloneDataTypeManager(String rootName) { + public StandAloneDataTypeManager(String rootName) throws RuntimeIOException { super(DataOrganizationImpl.getDefaultOrganization()); this.name = rootName; } @@ -149,8 +151,10 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos * Note that this manager does not support the save or saveAs operation. * @param rootName Name of the root category. * @param dataOrganzation applicable data organization + * @throws RuntimeIOException if database error occurs during creation */ - public StandAloneDataTypeManager(String rootName, DataOrganization dataOrganzation) { + public StandAloneDataTypeManager(String rootName, DataOrganization dataOrganzation) + throws RuntimeIOException { super(dataOrganzation); this.name = rootName; }