diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverPluginScreenShots.java index 29aba8549c..c6956671da 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverPluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverPluginScreenShots.java @@ -95,8 +95,8 @@ public class VariableValueHoverPluginScreenShots extends GhidraScreenShotGenerat protected void intoProject(DomainObject obj) { waitForDomainObject(obj); DomainFolder rootFolder = tool.getProject() - .getProjectData() - .getRootFolder(); + .getProjectData() + .getRootFolder(); waitForCondition(() -> { try { rootFolder.createFile(obj.getName(), obj, monitor); @@ -141,7 +141,7 @@ public class VariableValueHoverPluginScreenShots extends GhidraScreenShotGenerat try (Transaction tx = program.openTransaction("Assemble")) { Address entry = addr(program, 0x00400000); program.getMemory() - .createInitializedBlock(".text", entry, 0x1000, (byte) 0, monitor, false); + .createInitializedBlock(".text", entry, 0x1000, (byte) 0, monitor, false); Assembler asm = Assemblers.getAssembler(program.getLanguage(), StackUnwinderTest.NO_16BIT_CALLS); AssemblyBuffer buf = new AssemblyBuffer(asm, entry); @@ -193,9 +193,9 @@ public class VariableValueHoverPluginScreenShots extends GhidraScreenShotGenerat dis.disassemble(entry, null); Function function = program.getFunctionManager() - .createFunction("fib", entry, - new AddressSet(entry, entry.add(bytes.length - 1)), - SourceType.USER_DEFINED); + .createFunction("fib", entry, + new AddressSet(entry, entry.add(bytes.length - 1)), + SourceType.USER_DEFINED); function.updateFunction("__cdecl", new ReturnParameterImpl(UnsignedIntegerDataType.dataType, program), @@ -231,7 +231,7 @@ public class VariableValueHoverPluginScreenShots extends GhidraScreenShotGenerat DebuggerEmulationService emuService = addPlugin(tool, DebuggerEmulationServicePlugin.class); Function function = createFibonacciProgramX86_32(); - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); Address entry = function.getEntryPoint(); programManager.openProgram(program); @@ -271,9 +271,9 @@ public class VariableValueHoverPluginScreenShots extends GhidraScreenShotGenerat try (Transaction tx = tb.startTransaction()) { tb.trace.getBreakpointManager() - .addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), retInstr, - Set.of(), - Set.of(TraceBreakpointKind.SW_EXECUTE), true, "unwind stack"); + .addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), retInstr, + Set.of(), + Set.of(TraceBreakpointKind.SW_EXECUTE), true, "unwind stack"); } EmulationResult result = emuService.run(atSetup.getPlatform(), atSetup.getTime(), monitor, diff --git a/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm b/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm index 7851119982..a02bd3981b 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm @@ -128,6 +128,20 @@ Analyzers in the Analysis menu. Only those analyzers which support one-shot use and are applicable to the current program will be available within the One Shot sub-menu.
+ ++When opening a program for the first time, you will be asked if you want to analyze the + program. If you respond "Yes", The Auto Analysis Options + dialog will appear, allowing you to begin analyzing the program. If you decide not to + analyze the program, you have the choice of having Ghidra asking you again the next time + you open the program. If you pick the "No" options, Ghidra will continue to ask you to + every time you open the program. If you pick "No (Don't ask again)" Ghidra will never ask you to + analyze the program again, but you can still manually initiate analysis at any time. + If you choose not to have Ghidra ask again, it will set a property in the program called + "Should Ask To Analyze" to false. Since this changes a property in the program, the program + now has changes that need to be saved.
+
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java index c24c055bac..8e8aeb928b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java @@ -72,7 +72,8 @@ public class PropertyListMergeManager implements MergeResolver { * resultProgram and latestProgram start out the same */ public PropertyListMergeManager(ProgramMultiUserMergeManager mergeManager, - Program resultProgram, Program myProgram, Program originalProgram, Program latestProgram) { + Program resultProgram, Program myProgram, Program originalProgram, + Program latestProgram) { this.mergeManager = mergeManager; this.resultProgram = resultProgram; this.myProgram = myProgram; @@ -142,7 +143,8 @@ public class PropertyListMergeManager implements MergeResolver { currentMonitor.setProgress(i); String myName = myNames.get(i); int progress = (int) (((float) (i / myNamesCount)) * 100); - mergeManager.updateProgress(progress, "Merging property list for " + myName + "..."); + mergeManager.updateProgress(progress, + "Merging property list for " + myName + "..."); boolean isInLatest = latestNames.contains(myName); boolean isInOrig = origNames.contains(myName); if (!isInLatest && !isInOrig) { @@ -277,11 +279,6 @@ public class PropertyListMergeManager implements MergeResolver { // value was not modified in my program or it was changed the same as in latest return; } - if (propertyName.equals(Program.ANALYZED) && Boolean.TRUE.equals(myValue)) { - // If my version sets "Analyzed" to true, then it should result in true. - setValue(resultList, propertyName, myList.getType(propertyName), Boolean.TRUE); - return; - } if (SystemUtilities.isEqual(resultValue, origValue)) { // no change by latest - use my value setValue(resultList, propertyName, myList.getType(propertyName), myValue); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisBackgroundCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisBackgroundCommand.java index 120e48a727..887526c3c8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisBackgroundCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisBackgroundCommand.java @@ -53,7 +53,7 @@ public class AnalysisBackgroundCommand extends MergeableBackgroundCommand { @Override public boolean applyTo(DomainObject obj, TaskMonitor monitor) { if (markAsAnalyzed) { - GhidraProgramUtilities.setAnalyzedFlag((Program) obj, true); + GhidraProgramUtilities.markProgramAnalyzed((Program) obj); } mgr.startAnalysis(monitor); return true; @@ -63,12 +63,12 @@ public class AnalysisBackgroundCommand extends MergeableBackgroundCommand { @Override public MergeableBackgroundCommand mergeCommands(MergeableBackgroundCommand command) { SystemUtilities.assertTrue(command instanceof AnalysisBackgroundCommand, - "This code assumes that the " - + "two commands are both AnalysisBackgroundCommands and this is not the case."); + "This code assumes that the " + + "two commands are both AnalysisBackgroundCommands and this is not the case."); AnalysisBackgroundCommand abc = (AnalysisBackgroundCommand) command; - SystemUtilities.assertTrue(mgr == abc.mgr, "This code assumes that the " - + "managers of the two commands are the same instance and this is not the case."); + SystemUtilities.assertTrue(mgr == abc.mgr, "This code assumes that the " + + "managers of the two commands are the same instance and this is not the case."); // once we encounter a markAsAnalyzed value that is true, then leave it on markAsAnalyzed = markAsAnalyzed | abc.markAsAnalyzed; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java index 61172d1192..3cd46f7891 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java @@ -359,8 +359,8 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener { } File saveFile = getOptionsSaveFile(saveName); if (saveFile.exists() && OptionDialog.CANCEL_OPTION == OptionDialog - .showOptionDialogWithCancelAsDefaultButton(this, "Overwrite Configuration", - "Overwrite existing configuration file: " + saveName + " ?", "Overwrite")) { + .showOptionDialogWithCancelAsDefaultButton(this, "Overwrite Configuration", + "Overwrite existing configuration file: " + saveName + " ?", "Overwrite")) { return; } FileOptions currentOptions = getCurrentOptionsAsFileOptions(); @@ -687,7 +687,7 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener { GenericOptionsComponent.createOptionComponent(childState); HelpLocation helpLoc = analysisOptions - .getHelpLocation(analyzerName + Options.DELIMITER_STRING + childOptionName); + .getHelpLocation(analyzerName + Options.DELIMITER_STRING + childOptionName); if (helpLoc != null) { help.registerHelp(comp, helpLoc); } @@ -775,7 +775,7 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener { private boolean isAnalyzed() { Options options = programs.get(0).getOptions(Program.PROGRAM_INFO); - return options.getBoolean(Program.ANALYZED, false); + return options.getBoolean(Program.ANALYZED_OPTION_NAME, false); } private Options[] loadPossibleOptionsChoicesForComboBox() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java index 7af6077bb5..76bad7aaad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java @@ -132,7 +132,7 @@ class AnalyzeAllOpenProgramsTask extends Task { AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program); initializeAnalysisOptions(program, prototypeAnalysisOptions, manager); - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); analyzeStrategy.analyzeProgram(program, manager, monitor); } 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 16292e396b..27f0b84789 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 @@ -1048,8 +1048,8 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl } public void registerOptions() { - Options options = program.getOptions(Program.ANALYSIS_PROPERTIES); - registerOptions(options); + registerGlobalAnalyisOptions(); + registerAnalyzerOptions(); } public void initializeOptions() { @@ -1075,7 +1075,18 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl dataTasks.optionsChanged(options); } - public void registerOptions(Options options) { + private void registerGlobalAnalyisOptions() { + Options options = program.getOptions(Program.PROGRAM_INFO); + options.registerOption(Program.ANALYZED_OPTION_NAME, false, null, + "Indicates if program has ever been analyzed"); + + options.registerOption(Program.ASK_TO_ANALYZE_OPTION_NAME, true, null, + "Indicates if user should be prompted to analyze an unanalyzed program when opened"); + + } + + public void registerAnalyzerOptions() { + Options options = program.getOptions(Program.ANALYSIS_PROPERTIES); byteTasks.registerOptions(options); functionTasks.registerOptions(options); functionModifierChangedTasks.registerOptions(options); @@ -1106,13 +1117,16 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl Swing.assertSwingThread("Asking to analyze must be on the swing thread!"); if (GhidraProgramUtilities.shouldAskToAnalyze(program)) { - // initialize the analyzed flag to a non-null value to indicate we at least asked - GhidraProgramUtilities.setAnalyzedFlag(program, false); + String name = HTMLUtilities.escapeHTML(program.getDomainFile().getName()); + HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Ask_To_Analyze"); + int result = OptionDialog.showOptionNoCancelDialog(tool.getToolFrame(), "Analyze?", + "" + name + " has not been analyzed. Would you like to analyze it now?", + "Yes", "No", "No (Don't ask again)", OptionDialog.QUESTION_MESSAGE, help); - int answer = OptionDialog.showYesNoDialog(tool.getToolFrame(), "Analyze", - "" + HTMLUtilities.escapeHTML(program.getDomainFile().getName()) + - " has not been analyzed. Would you like to analyze it now?"); - return answer == OptionDialog.OPTION_ONE; //Analyze + if (result == OptionDialog.OPTION_THREE) { + GhidraProgramUtilities.markProgramNotToAskToAnalyze(program); + } + return result == OptionDialog.OPTION_ONE; //Analyze } return false; } @@ -1271,12 +1285,12 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl testLen = spacer.length() - 5; } taskTimesStringBuf - .append(" " + element + spacer.substring(testLen) + secString + "\n"); + .append(" " + element + spacer.substring(testLen) + secString + "\n"); } taskTimesStringBuf.append("-----------------------------------------------------\n"); taskTimesStringBuf - .append(" Total Time " + (int) (totalTaskTime / 1000.00) + " secs\n"); + .append(" Total Time " + (int) (totalTaskTime / 1000.00) + " secs\n"); taskTimesStringBuf.append("-----------------------------------------------------\n"); return taskTimesStringBuf.toString(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java index d017c0c7aa..8bb73371ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java @@ -188,7 +188,7 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis // check if this is the first time this program is being analyzed. If so, // schedule a callback when it is completed to send a FirstTimeAnalyzedPluginEvent - boolean isAnalyzed = GhidraProgramUtilities.isAnalyzedFlagSet(program); + boolean isAnalyzed = GhidraProgramUtilities.isAnalyzed(program); if (!isAnalyzed) { analysisMgr.addListener(new FirstTimeAnalyzedCallback()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java index a88978f0b8..6ebee30d2e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java @@ -457,7 +457,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider { // // Cancel // - if (choice == OptionDialog.OPTION_THREE) { + if (choice == OptionDialog.CANCEL_OPTION) { // Cancel return; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java index 504f3c9823..8149608689 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java @@ -878,7 +878,7 @@ public class HeadlessAnalyzer { // Get parent folder to pass to GhidraScript File parentFile = new File(c.getResource(c.getSimpleName() + ".class").toURI()) - .getParentFile(); + .getParentFile(); currScript = (GhidraScript) c.getConstructor().newInstance(); currScript.setScriptArgs(scriptArgs); @@ -1019,7 +1019,7 @@ public class HeadlessAnalyzer { mgr.startAnalysis(TaskMonitor.DUMMY); // kick start Msg.info(this, "REPORT: Analysis succeeded for file: " + fileAbsolutePath); - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); } else { HeadlessTimedTaskMonitor timerMonitor = @@ -1042,7 +1042,7 @@ public class HeadlessAnalyzer { timerMonitor.cancel(); Msg.info(this, "REPORT: Analysis succeeded for file: " + fileAbsolutePath); - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); } } } @@ -1559,7 +1559,7 @@ public class HeadlessAnalyzer { return false; } } - + // Save for (Loadedloaded : loadResults) { if (!loaded.getDomainObject().isTemporary()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java index 708a00e36d..5602e070fb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java @@ -124,7 +124,6 @@ public abstract class AbstractProgramLoader implements Loader { MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException, CancelledException, VersionException, LoadException { - if (!loadSpec.isComplete()) { throw new LoadException("Load spec is incomplete"); } @@ -369,7 +368,7 @@ public abstract class AbstractProgramLoader implements Loader { fsrl = fsrl.withMD5(md5); } prog.getOptions(Program.PROGRAM_INFO) - .setString(ProgramMappingService.PROGRAM_SOURCE_FSRL, fsrl.toString()); + .setString(ProgramMappingService.PROGRAM_SOURCE_FSRL, fsrl.toString()); } prog.setExecutableMD5(md5); String sha256 = computeBinarySHA256(provider); @@ -498,17 +497,19 @@ public abstract class AbstractProgramLoader implements Loader { boolean anchorSymbols = shouldAnchorSymbols(options); List labels = lang.getDefaultSymbols(); for (AddressLabelInfo info : labels) { - createSymbol(program, info.getLabel(), info.getAddress(), info.isEntry(), info.isPrimary(), anchorSymbols); + createSymbol(program, info.getLabel(), info.getAddress(), info.isEntry(), + info.isPrimary(), anchorSymbols); } } - GhidraProgramUtilities.removeAnalyzedFlag(program); + GhidraProgramUtilities.resetAnalysisFlags(program); } finally { program.endTransaction(id, true); } } - private static void createSymbol(Program program, String labelname, Address address, boolean isEntry, boolean isPrimary, boolean anchorSymbols) { + private static void createSymbol(Program program, String labelname, Address address, + boolean isEntry, boolean isPrimary, boolean anchorSymbols) { SymbolTable symTable = program.getSymbolTable(); Address addr = address; Symbol s = symTable.getPrimarySymbol(addr); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/ProgramXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/ProgramXmlMgr.java index f64f2b4f2b..e7a2f1b85f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/ProgramXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/ProgramXmlMgr.java @@ -389,7 +389,7 @@ public class ProgramXmlMgr { //if instructions were imported, then remove the "needs analyzed" property if (options.isInstructions()) { - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); } return log; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/tasks/GFileSystemLoadKernelTask.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/tasks/GFileSystemLoadKernelTask.java index b9b215576b..fab65a844e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/tasks/GFileSystemLoadKernelTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/tasks/GFileSystemLoadKernelTask.java @@ -15,9 +15,8 @@ */ package ghidra.plugins.fsbrowser.tasks; -import java.util.List; - import java.io.IOException; +import java.util.List; import ghidra.app.services.ProgramManager; import ghidra.formats.gfilesystem.*; @@ -140,7 +139,7 @@ public class GFileSystemLoadKernelTask extends Task { file.getParentFile().getPath()); String fileName = ProjectDataUtils.getUniqueName(folder, program.getName()); - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); folder.createFile(fileName, program, monitor); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/database/ProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/program/database/ProgramBuilder.java index 3f49b4e331..728dd421fb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/database/ProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/database/ProgramBuilder.java @@ -136,10 +136,10 @@ public class ProgramBuilder { CompilerSpec compilerSpec = compilerSpecID == null ? language.getDefaultCompilerSpec() : language.getCompilerSpecByID(new CompilerSpecID(compilerSpecID)); program = new ProgramDB(name, language, compilerSpec, consumer == null ? this : consumer); - setAnalyzed(true); + setAnalyzed(); program.setTemporary(true); // ignore changes } - + /** * Construct program builder using a full language object rather than a language id string * @param name program name @@ -150,7 +150,7 @@ public class ProgramBuilder { throws Exception { CompilerSpec compilerSpec = language.getDefaultCompilerSpec(); program = new ProgramDB(name, language, compilerSpec, this); - setAnalyzed(true); + setAnalyzed(); program.setTemporary(true); // ignore changes } @@ -281,10 +281,9 @@ public class ProgramBuilder { /** * This prevents the 'ask to analyze' dialog from showing when called with {@code true} - * @param analyzed true to mark the program as analyzed */ - public void setAnalyzed(boolean analyzed) { - GhidraProgramUtilities.setAnalyzedFlag(program, analyzed); + public void setAnalyzed() { + GhidraProgramUtilities.markProgramAnalyzed(program); } public MemoryBlock createMemory(String name, String address, int size) { @@ -321,8 +320,8 @@ public class ProgramBuilder { return tx(() -> { return program.getMemory() - .createInitializedBlock(name, addr(address), size, (byte) 0, TaskMonitor.DUMMY, - true); + .createInitializedBlock(name, addr(address), size, (byte) 0, TaskMonitor.DUMMY, + true); }); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/GhidraProgramUtilities.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/GhidraProgramUtilities.java index c9de4fa333..844cd46bd0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/GhidraProgramUtilities.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/GhidraProgramUtilities.java @@ -25,8 +25,9 @@ public class GhidraProgramUtilities { } /** - * returns the current program, given a tool, if a program is opened; - * otherwise returns null. + * Returns the current program for the given tool or null if no program is open. + * @param tool the tool get get the current program for + * @return the current program for the given tool or null if no program is open */ public static Program getCurrentProgram(PluginTool tool) { ProgramManager pmService = tool.getService(ProgramManager.class); @@ -34,11 +35,13 @@ public class GhidraProgramUtilities { } /** - * Returns true if the program does not contain the analyzed flag. The assumption is that a - * non-null value means that the user has already made a decision about analyzing. + * Returns true if the user should be asked to analyze. They will only be asked if the program + * hasn't already been analyzed (analyzed flag property is false or null) or the + * "ask to analyze" flag property is true or null (default is true unless explicitly set to + * false). * * @param program the program to check for the property - * @return true if the program does not contain the analyzed flag + * @return true if the user should be prompted to analyze the program */ public static boolean shouldAskToAnalyze(Program program) { @@ -48,20 +51,27 @@ public class GhidraProgramUtilities { } Options options = program.getOptions(Program.PROGRAM_INFO); - return !options.contains(Program.ANALYZED); + // older programs don't have a "Ask" property, so check analyzed flag + boolean isAnalyzed = options.getBoolean(Program.ANALYZED_OPTION_NAME, false); + if (isAnalyzed) { + return false; + } + return options.getBoolean(Program.ASK_TO_ANALYZE_OPTION_NAME, true); } /** - * Removes the analyzed flag from the program properties. - * With this property removed, the user will be prompted to analyze the + * Resets the analysis flags to the program defaults + * With this reset, the user will be prompted to analyze the * program the next time it is opened. - * @param program the program containing the property to be removed + * @param program the program whose analysis flags should be reset */ - public static void removeAnalyzedFlag(Program program) { - int transactionID = program.startTransaction(Program.ANALYZED); + public static void resetAnalysisFlags(Program program) { + int transactionID = program.startTransaction("Reset Analysis Flags"); + try { Options options = program.getOptions(Program.PROGRAM_INFO); - options.removeOption(Program.ANALYZED); + options.removeOption(Program.ANALYZED_OPTION_NAME); + options.removeOption(Program.ASK_TO_ANALYZE_OPTION_NAME); } finally { program.endTransaction(transactionID, true); @@ -69,21 +79,28 @@ public class GhidraProgramUtilities { } /** - * Sets the analyzed flag to the specified value. + * Marks the program has having been analyzed * @param program the program to set property - * @param analyzed the analyzed flag */ - public static void setAnalyzedFlag(Program program, boolean analyzed) { + public static void markProgramAnalyzed(Program program) { Options options = program.getOptions(Program.PROGRAM_INFO); - // once the program is analyzed, register the value, so it won't keep writing it to the database. - if (analyzed && !options.isRegistered(Program.ANALYZED)) { - options.registerOption(Program.ANALYZED, false, null, - "Indicates if program has been analyzed"); - } - int transactionID = program.startTransaction(Program.ANALYZED); + int transactionID = program.startTransaction("Mark Program Analyzed"); try { - options.setBoolean(Program.ANALYZED, analyzed); + options.setBoolean(Program.ANALYZED_OPTION_NAME, true); + options.setBoolean(Program.ASK_TO_ANALYZE_OPTION_NAME, false); + } + finally { + program.endTransaction(transactionID, true); + } + } + + public static void markProgramNotToAskToAnalyze(Program program) { + Options options = program.getOptions(Program.PROGRAM_INFO); + + int transactionID = program.startTransaction("Mark Program To Not Ask To Analyze"); + try { + options.setBoolean(Program.ASK_TO_ANALYZE_OPTION_NAME, false); } finally { program.endTransaction(transactionID, true); @@ -95,17 +112,10 @@ public class GhidraProgramUtilities { * @param program the program to test to see if it has been analyzed * @return true if the program has been analyzed at least once. */ - public static boolean isAnalyzedFlagSet(Program program) { + public static boolean isAnalyzed(Program program) { Options options = program.getOptions(Program.PROGRAM_INFO); - // we first have to check if the flag has even been created because checking the flag - // directly causes it to be created if it doesn't exist and we unfortunately use the - // existence of the flag to know whether or not to ask the user if they want to start - // analysis - if (!options.isRegistered(Program.ANALYZED)) { - return false; - } - - return options.getBoolean(Program.ANALYZED, false); + return options.getBoolean(Program.ANALYZED_OPTION_NAME, false); } + } 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..8b8545200c 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 @@ -256,9 +256,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E // By default, create test output within a directory at the same level as the // development repositories outputRoot = Application.getApplicationRootDirectory() - .getParentFile() - .getParentFile() - .getCanonicalPath(); + .getParentFile() + .getParentFile() + .getCanonicalPath(); } catch (IOException e) { throw new RuntimeException(e); @@ -1562,7 +1562,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E setAnalysisOptions(program.getOptions(Program.ANALYSIS_PROPERTIES)); - GhidraProgramUtilities.setAnalyzedFlag(program, true); + GhidraProgramUtilities.markProgramAnalyzed(program); // Remove all single-byte functions created by Elf importer // NOTE: This is a known issues with optimized code and symbols marked as ElfSymbol.STT_FUNC @@ -1670,7 +1670,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E RegisterValue thumbMode = new RegisterValue(tReg, BigInteger.ONE); try { program.getProgramContext() - .setRegisterValue(functionAddr, functionAddr, thumbMode); + .setRegisterValue(functionAddr, functionAddr, thumbMode); } catch (ContextChangeException e) { throw new AssertException(e); @@ -1684,7 +1684,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E RegisterValue thumbMode = new RegisterValue(isaModeReg, BigInteger.ONE); try { program.getProgramContext() - .setRegisterValue(functionAddr, functionAddr, thumbMode); + .setRegisterValue(functionAddr, functionAddr, thumbMode); } catch (ContextChangeException e) { throw new AssertException(e); @@ -1909,7 +1909,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E if (absoluteGzfFilePath.exists()) { program = getGzfProgram(outputDir, gzfCachePath); if (program != null && !MD5Utilities.getMD5Hash(testFile.file) - .equals(program.getExecutableMD5())) { + .equals(program.getExecutableMD5())) { // remove obsolete GZF cache file env.release(program); program = null; @@ -1934,7 +1934,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E } else { program = env.getGhidraProject() - .importProgram(testFile.file, language, compilerSpec); + .importProgram(testFile.file, language, compilerSpec); } program.addConsumer(this); env.getGhidraProject().close(program); @@ -1955,8 +1955,8 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E if (!program.getLanguageID().equals(language.getLanguageID()) || !program.getCompilerSpec() - .getCompilerSpecID() - .equals(compilerSpec.getCompilerSpecID())) { + .getCompilerSpecID() + .equals(compilerSpec.getCompilerSpecID())) { throw new IOException((usingCachedGZF ? "Cached " : "") + "Program has incorrect language/compiler spec (" + program.getLanguageID() + "/" + program.getCompilerSpec().getCompilerSpecID() + "): " + @@ -2121,7 +2121,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E testFileDigest.append(nameAndAddr); testFileDigest.append(" (GroupInfo @ "); testFileDigest - .append(testGroup.controlBlock.getInfoStructureAddress().toString(true)); + .append(testGroup.controlBlock.getInfoStructureAddress().toString(true)); testFileDigest.append(")"); if (duplicateTests.contains(testGroup.testGroupName)) { testFileDigest.append(" *DUPLICATE*"); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java index 691a9b7133..c0a0e69fc0 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java @@ -18,6 +18,7 @@ package ghidra.app.util.pdb; import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.framework.options.Options; import ghidra.program.model.listing.Program; +import ghidra.program.util.GhidraProgramUtilities; /** * Storage of PDB-related attributes @@ -47,9 +48,7 @@ public class PdbProgramAttributes { pdbLoaded = propList.contains(PdbParserConstants.PDB_LOADED) ? propList.getBoolean(PdbParserConstants.PDB_LOADED, false) : false; - programAnalyzed = propList.contains(Program.ANALYZED) - ? propList.getBoolean(Program.ANALYZED, false) - : false; + programAnalyzed = GhidraProgramUtilities.isAnalyzed(program); pdbSignature = propList.contains(PdbParserConstants.PDB_SIGNATURE) ? propList.getString(PdbParserConstants.PDB_SIGNATURE, null) : null; @@ -141,5 +140,4 @@ public class PdbProgramAttributes { return programAnalyzed; } - } diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java index 8f733ba1ba..8024da113e 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java @@ -15,12 +15,11 @@ */ package pdb; -import java.util.*; -import java.util.stream.Collectors; - import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.stream.Collectors; import javax.swing.SwingConstants; @@ -39,6 +38,7 @@ import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.preferences.Preferences; import ghidra.program.model.listing.Program; +import ghidra.program.util.GhidraProgramUtilities; import ghidra.util.HelpLocation; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; @@ -75,22 +75,22 @@ public class PdbPlugin extends Plugin { private void createActions() { new ActionBuilder("Load PDB File", this.getName()) - .supportsDefaultToolContext(true) - .withContext(ProgramActionContext.class) - .validContextWhen(pac -> pac.getProgram() != null && - PdbAnalyzerCommon.canAnalyzeProgram(pac.getProgram())) - .menuPath(ToolConstants.MENU_FILE, "Load PDB File...") - .menuGroup("Import PDB", "3") - .helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Load PDB File")) - .onAction(pac -> loadPDB(pac)) - .buildAndInstall(tool); + .supportsDefaultToolContext(true) + .withContext(ProgramActionContext.class) + .validContextWhen(pac -> pac.getProgram() != null && + PdbAnalyzerCommon.canAnalyzeProgram(pac.getProgram())) + .menuPath(ToolConstants.MENU_FILE, "Load PDB File...") + .menuGroup("Import PDB", "3") + .helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Load PDB File")) + .onAction(pac -> loadPDB(pac)) + .buildAndInstall(tool); new ActionBuilder("Symbol Server Config", this.getName()) - .menuPath(ToolConstants.MENU_EDIT, "Symbol Server Config") - .menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP) - .helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config")) - .onAction(ac -> configPDB()) - .buildAndInstall(tool); + .menuPath(ToolConstants.MENU_EDIT, "Symbol Server Config") + .menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP) + .helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config")) + .onAction(ac -> configPDB()) + .buildAndInstall(tool); } private void configPDB() { @@ -107,8 +107,7 @@ public class PdbPlugin extends Plugin { return; } - boolean analyzed = - program.getOptions(Program.PROGRAM_INFO).getBoolean(Program.ANALYZED, false); + boolean analyzed = GhidraProgramUtilities.isAnalyzed(program); if (analyzed) { int response = @@ -147,8 +146,8 @@ public class PdbPlugin extends Plugin { LoadPdbTask loadPdbTask = new LoadPdbTask(program, pdbFile, loadPdbResults.useMsDiaParser, loadPdbResults.control, dataTypeManagerService); TaskBuilder.withTask(loadPdbTask) - .setStatusTextAlignment(SwingConstants.LEADING) - .setLaunchDelay(0); + .setStatusTextAlignment(SwingConstants.LEADING) + .setLaunchDelay(0); new TaskLauncher(loadPdbTask, null, 0); // Check for error messages & exceptions and handle them here @@ -282,15 +281,15 @@ public class PdbPlugin extends Plugin { SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) { SymbolServer temporarySymbolServer = symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry() - .newSymbolServer(Preferences.getProperty(SYMBOL_STORAGE_DIR_OPTION, "", true), - symbolServerInstanceCreatorContext); + .newSymbolServer(Preferences.getProperty(SYMBOL_STORAGE_DIR_OPTION, "", true), + symbolServerInstanceCreatorContext); SymbolStore symbolStore = (temporarySymbolServer instanceof SymbolStore) ? (SymbolStore) temporarySymbolServer : new SameDirSymbolStore(symbolServerInstanceCreatorContext.getRootDir()); List symbolServers = symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry() - .createSymbolServersFromPathList(getSymbolSearchPaths(), - symbolServerInstanceCreatorContext); + .createSymbolServersFromPathList(getSymbolSearchPaths(), + symbolServerInstanceCreatorContext); return new SymbolServerService(symbolStore, symbolServers); } @@ -307,9 +306,9 @@ public class PdbPlugin extends Plugin { symbolServerService.getSymbolStore().getName()); String path = symbolServerService.getSymbolServers() - .stream() - .map(SymbolServer::getName) - .collect(Collectors.joining(";")); + .stream() + .map(SymbolServer::getName) + .collect(Collectors.joining(";")); Preferences.setProperty(SYMBOL_SEARCH_PATH_OPTION, path); } else { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialog.java index 3b082a0df1..c778c81fb3 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/OptionDialog.java @@ -310,7 +310,7 @@ public class OptionDialog extends DialogComponentProvider { return ps; } }; - + panel.add(label, BorderLayout.WEST); panel.add(textPanel, BorderLayout.CENTER); return panel; @@ -764,7 +764,7 @@ public class OptionDialog extends DialogComponentProvider { return Swing.runNow(() -> { OptionDialog info = new OptionDialog(title, message, option1, option2, PLAIN_MESSAGE, icon, false); - return info.show(); + return info.show(parent); }); } @@ -794,7 +794,39 @@ public class OptionDialog extends DialogComponentProvider { return Swing.runNow(() -> { OptionDialog info = new OptionDialog(title, message, option1, option2, option3, messageType, null, false); - return info.show(); + return info.show(parent); + }); + } + + /** + * Static helper method to easily display an three-option dialog with no Cancel button. + * The dialog will remain until the user presses the + * Option1, Option 2, or Option 3 button. + * + * @param parent The parent component of this dialog. If the given component is + * a frame or dialog, then the component will be used to parent the option dialog. + * Otherwise, the parent frame or dialog will be found by traversing up the given + * component's parent hierarchy. Also, null can be used to not parent the dialog at all, + * but this promotes poor dialog behavior + * @param title The String to be placed in the dialogs title area + * @param message The information message to be displayed in the dialog + * @param option1 The text to place on the first option button + * @param option2 The text to place on the second option button + * @param option3 The text to place on the third option button + * @param messageType used to specify a default icon, can be ERROR_MESSAGE, + * INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE) + * @param help The help location for this dialog + * @return The options selected by the user. 1 for the first option and + * 2 for the second option. 0 is returned if the operation is cancelled + */ + public static int showOptionNoCancelDialog(Component parent, String title, String message, + String option1, String option2, String option3, int messageType, HelpLocation help) { + + return Swing.runNow(() -> { + OptionDialog info = new OptionDialog(title, message, option1, option2, option3, + messageType, null, false); + info.setHelpLocation(help); + return info.show(parent); }); } 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..cdcfad4752 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 @@ -51,26 +51,28 @@ public interface Program extends DataTypeManagerDomainObject { public static final String ANALYSIS_PROPERTIES = "Analyzers"; public static final String DISASSEMBLER_PROPERTIES = "Disassembler"; - /** Name of program information property list */ + /** Options for storing program info */ public static final String PROGRAM_INFO = "Program Information"; - /** Name of program settings property list */ - public static final String PROGRAM_SETTINGS = "Program Settings"; + /** Name of boolean analyzed property */ - public static final String ANALYZED = "Analyzed"; - /** Name of date created property */ + public static final String ANALYZED_OPTION_NAME = "Analyzed"; + /** Property to control if user should be asked to analyze when unanalyzed program opened */ + public static final String ASK_TO_ANALYZE_OPTION_NAME = "Should Ask To Analyze"; + + /** Date created property */ public static final String DATE_CREATED = "Date Created"; - /** Name of ghidra version property */ + /** Ghidra version property */ public static final String CREATED_WITH_GHIDRA_VERSION = "Created With Ghidra Version"; - /** Name of ghidra preferred root namespace category property */ + /** Ghidra preferred root namespace category property */ public static final String PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY = "Preferred Root Namespace Category"; - /** Creation date to ask for analysis */ + + /** Creation date for analysis */ public static final String ANALYSIS_START_DATE = "2007-Jan-01"; /** Format string of analysis date */ public static final String ANALYSIS_START_DATE_FORMAT = "yyyy-MMM-dd"; /** A date from January 1, 1970 */ public static final Date JANUARY_1_1970 = new Date(0); - /** The maximum number of operands for any assembly language */ public final static int MAX_OPERANDS = 16; diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java index 280d9d7c78..d9127a6c9f 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java @@ -49,7 +49,7 @@ public class PdbScreenShots extends GhidraScreenShotGenerator { temporaryDir = createTempDirectory("example_pdb"); tx = program.startTransaction("set analyzed flag"); Options proplist = program.getOptions(Program.PROGRAM_INFO); - proplist.setBoolean(Program.ANALYZED, false); + proplist.setBoolean(Program.ANALYZED_OPTION_NAME, false); PdbInfo pdbInfo = PdbInfoDotNet.fromValues("HelloWorld.pdb", 1, new GUID(GUID1_STR)); pdbInfo.serializeToOptions(proplist); proplist.setString("Executable Location",