GP-1689 - Added analysis options update mechanism to allow for changing option names and types

This commit is contained in:
dragonmacher
2022-02-14 15:34:28 -05:00
parent c2510e5ab0
commit 9bb2429eb3
12 changed files with 940 additions and 129 deletions
@@ -0,0 +1,127 @@
/* ###
* 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.analysis;
import java.util.*;
import java.util.function.Function;
import ghidra.framework.options.Options;
/**
* An object that allows analyzers to rename options. This is required to move old options stored
* in the program to the new equivalent option. This class is not required for options that have
* simply been removed.
* <p>
* Notes:
* <ul>
* <li>
* Replacement options must be registered with one of the register methods of this class.
* </li>
* <li>
* This is intended for use with the UI; access analysis options from the API will not use this
* replacer. This means that any client, such as script, retrieving the old option value will not
* work for new programs that no longer have that old option registered. Further, for programs
* that have the old options saved, but no longer registered, changing the old option value will
* have no effect.
* </li>
* <li>
* Old option values will only be used if they are non-default and the new option value is default.
* </li>
* <li>
* Clients can change the type of the option if they wish using
* {@link #registerReplacement(String, String, Function)}.
* </li>
* </ul>
*/
public class AnalysisOptionsUpdater {
private static final Function<Object, Object> OLD_VALUE_REPLACER = oldValue -> oldValue;
private Map<String, ReplaceableOption> optionsByNewName = new HashMap<>();
/**
* Register the given old option name to be replaced with the new option name. The
* replacement strategy used in this case will be to return the old value for the new option.
* @param newOptionName the new option name
* @param oldOptionName the old option name
*/
public void registerReplacement(String newOptionName, String oldOptionName) {
registerReplacement(newOptionName, oldOptionName, OLD_VALUE_REPLACER);
}
/**
* Register the given old option name to be replaced with the new option name. The given
* replacer function will be called with the old option value to get the new option value.
* @param newOptionName the new option name
* @param oldOptionName the old option name
* @param replacer the function to update the update the old option value
*/
public void registerReplacement(String newOptionName, String oldOptionName,
Function<Object, Object> replacer) {
optionsByNewName.put(newOptionName,
new ReplaceableOption(newOptionName, oldOptionName, replacer));
}
Set<ReplaceableOption> getReplaceableOptions() {
return new HashSet<>(optionsByNewName.values());
}
/**
* A simple object that contains the new and old option name along with the replacer function
* that will handle the option replacement.
*/
public static class ReplaceableOption {
private final String newName;
private final String oldName;
private final Function<Object, Object> replacer;
ReplaceableOption(String newName, String oldName, Function<Object, Object> replacer) {
this.newName = newName;
this.oldName = oldName;
this.replacer = replacer;
}
// note: this method expects to be called within a transaction
void replace(Options options) {
Object oldValue = options.getObject(oldName, null);
if (oldValue == null) {
return;
}
if (options.isDefaultValue(oldName)) {
return;
}
if (!options.isDefaultValue(newName)) {
return; // don't overwrite user's updated value
}
Object newValue = replacer.apply(oldValue);
options.putObject(newName, newValue);
}
String getNewName() {
return newName;
}
String getOldName() {
return oldName;
}
}
}
@@ -40,6 +40,7 @@ import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GLabel;
import docking.widgets.table.GTable;
import ghidra.GhidraOptions;
import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater.ReplaceableOption;
import ghidra.app.services.Analyzer;
import ghidra.framework.Application;
import ghidra.framework.GenericRunInfo;
@@ -70,13 +71,15 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
// preference which retains last used analyzer_options file name
public static final String LAST_USED_OPTIONS_CONFIG = "LAST_USED_OPTIONS_CONFIG";
static final String ANALYZER_OPTIONS_PANEL_NAME = "analyzer.options.panel";
private List<Program> programs;
private PropertyChangeListener propertyChangeListener;
private Options analysisOptions;
private Options currentProgramOptions; // this will have all the non-default options from the program
private Options selectedOptions = STANDARD_DEFAULT_OPTIONS;
private JTable table;
private GTable table;
private AnalysisEnablementTableModel model;
private JTextArea descriptionComponent;
private JPanel analyzerOptionsPanel;
@@ -130,6 +133,9 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
currentProgramOptions = getNonDefaultProgramOptions();
setName("Analysis Panel");
build();
replaceOldOptions();
load();
loadCurrentOptionsIntoEditors();
}
@@ -169,13 +175,13 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
Program program = programs.get(0);
AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
List<String> propertyNames = analysisOptions.getOptionNames();
Collections.sort(propertyNames, (o1, o2) -> o1.compareToIgnoreCase(o2));
for (String analyzerName : propertyNames) {
List<String> optionNames = analysisOptions.getOptionNames();
Collections.sort(optionNames, (o1, o2) -> o1.compareToIgnoreCase(o2));
for (String analyzerName : optionNames) {
if (analyzerName.indexOf('.') == -1) {
if (analysisOptions.getType(analyzerName) != OptionType.BOOLEAN_TYPE) {
throw new AssertException(
"Analyzer enable property that is not boolean - " + analyzerName);
"Analyzer 'enable' property that is not boolean - " + analyzerName);
}
Analyzer analyzer = manager.getAnalyzer(analyzerName);
@@ -408,7 +414,7 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
private void reloadOptionsCombo(Options newDefaultOptions) {
optionConfigurationChoices = loadPossibleOptionsChoicesForComboBox();
optionsComboBox.setModel(new DefaultComboBoxModel<Options>(optionConfigurationChoices));
optionsComboBox.setModel(new DefaultComboBoxModel<>(optionConfigurationChoices));
Options selected = findOptionsByName(newDefaultOptions.getName());
optionsComboBox.setSelectedItem(selected);
}
@@ -587,6 +593,66 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
}
}
private void replaceOldOptions() {
for (Program program : programs) {
boolean commit = false;
int id = program.startTransaction("Replacing old analysis properties");
try {
doReplaceOldOptions(program);
commit = true;
}
finally {
program.endTransaction(id, commit);
}
}
}
private void doReplaceOldOptions(Program program) {
AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
Options programAnalysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
List<Options> allAnalyzersOptions = programAnalysisOptions.getChildOptions();
for (Options analyzerOptions : allAnalyzersOptions) {
String analyzerName = analyzerOptions.getName();
Analyzer analyzer = manager.getAnalyzer(analyzerName);
if (analyzer == null) {
// can be null if an analyzer no longer exists
continue;
}
AnalysisOptionsUpdater updater = analyzer.getOptionsUpdater();
if (updater == null) {
continue;
}
applyOptionUpdater(analyzerOptions, updater);
}
}
private void applyOptionUpdater(Options analyzerOptions, AnalysisOptionsUpdater updater) {
Set<ReplaceableOption> replaceableOptions = updater.getReplaceableOptions();
for (ReplaceableOption ro : replaceableOptions) {
String newName = ro.getNewName();
String oldName = ro.getOldName();
if (!analyzerOptions.contains(oldName)) {
continue; // the old option was never saved or has been removed
}
if (!analyzerOptions.contains(newName)) {
Msg.error(this,
"Found an option replacer without having the new option registered" +
"new option: '" + newName + "'; old option: '" + oldName + "'");
continue;
}
ro.replace(analyzerOptions);
analyzerOptions.removeOption(ro.getOldName());
}
}
private void loadAnalyzerOptionsPanels() {
List<Options> optionGroups = analysisOptions.getChildOptions();
noOptionsPanel = new JPanel(new VerticalLayout(5));
@@ -599,6 +665,7 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
String analyzerName = optionsGroup.getName();
JPanel optionsContainer = new JPanel(new VerticalLayout(5));
optionsContainer.setName(ANALYZER_OPTIONS_PANEL_NAME);
optionsContainer.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
JScrollPane scrollPane = new JScrollPane(optionsContainer);
@@ -645,11 +712,17 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
List<String> subOptions = optionsGroup.getLeafOptionNames();
Iterator<String> it = subOptions.iterator();
while (it.hasNext()) {
String next = it.next();
if (!isEditable(optionsGroup, next)) {
String name = it.next();
if (!isEditable(optionsGroup, name)) {
it.remove();
}
// also filter out unregistered options
if (!optionsGroup.isRegistered(name)) {
it.remove();
}
}
return subOptions;
}
@@ -90,8 +90,7 @@ class AnalyzeAllOpenProgramsTask extends Task {
return; // no need to log this - it's a valid condition
}
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(prototypeProgram);
if (!setOptions(prototypeProgram, mgr)) {
if (!setOptions(prototypeProgram)) {
return;
}
@@ -169,7 +168,7 @@ class AnalyzeAllOpenProgramsTask extends Task {
return true;
}
private boolean setOptions(final Program program, AutoAnalysisManager mgr) {
private boolean setOptions(final Program program) {
AtomicBoolean analyze = new AtomicBoolean();
int id = program.startTransaction("analysis");
try {
@@ -63,21 +63,15 @@ import ghidra.util.task.TaskLauncher;
public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerListener {
private static final String SHOW_ANALYSIS_OPTIONS = "Show Analysis Options";
private static final String ANALYZE_GROUP_NAME = "Analyze";
private DockingAction autoAnalyzeAction;
private DockingAction analyzeAllAction;
private HelpLocation helpLocation;
private List<Analyzer> analyzers = new ArrayList<>();
private List<OneShotAnalyzerAction> oneShotActions = new ArrayList<>();
/**
* Creates a new instance of the plugin giving it the tool that it will work
* in.
*/
public AutoAnalysisPlugin(PluginTool tool) {
super(tool);
@@ -125,8 +119,7 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
.onAction(this::analyzeCallback)
.buildAndInstall(tool);
analyzeAllAction =
new ActionBuilder("Analyze All Open", getName())
new ActionBuilder("Analyze All Open", getName())
.supportsDefaultToolContext(true)
.menuPath("&Analysis", "Analyze All &Open...")
.menuGroup(ANALYZE_GROUP_NAME, "" + subGroupIndex++)
@@ -206,31 +199,18 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
analysisMgr.reAnalyzeAll(selection);
}
/**
* Get the description of this plugin.
*/
public static String getDescription() {
return "Provides coordination and a service for All Auto Analysis tasks";
}
/**
* Get the descriptive name.
*/
public static String getDescriptiveName() {
return "AutoAnalysisManager";
}
/**
* Get the category.
*/
public static String getCategory() {
return "Analysis";
}
/***************************************************************************
* Implementation of AutoAnalysis Service
*/
protected void programClosed(Program program) {
if (AutoAnalysisManager.hasAutoAnalysisManager(program)) {
@@ -276,9 +256,10 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
private void programActivated(final Program program) {
program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST).registerOption(
StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
"Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST)
.registerOption(
StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
"Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
// invokeLater() to ensure that all other plugins have been notified of the program
// activated. This makes sure plugins like the Listing have opened and painted the
@@ -36,50 +36,79 @@ import ghidra.util.task.TaskMonitor;
public class DWARFAnalyzer extends AbstractAnalyzer {
private static final String DWARF_LOADED_OPTION_NAME = "DWARF Loaded";
private static final String OPTION_IMPORT_DATATYPES = "Import data types";
private static final String OPTION_IMPORT_DATATYPES = "Import Data Types";
private static final String OPTION_IMPORT_DATATYPES_DESC =
"Import data types defined in the DWARF debug info.";
private static final String OPTION_PRELOAD_ALL_DIES = "Preload all DIEs";
private static final String OPTION_PRELOAD_ALL_DIES = "Preload All DIEs";
private static final String OPTION_PRELOAD_ALL_DIES_DESC =
"Preload all DIE records. Requires more memory, but necessary for some non-standard layouts.";
"Preload all DIE records. Requires more memory, but necessary for some non-standard " +
"layouts.";
private static final String OPTION_IMPORT_FUNCS = "Import functions";
private static final String OPTION_IMPORT_FUNCS = "Import Functions";
private static final String OPTION_IMPORT_FUNCS_DESC =
"Import function information defined in the DWARF debug info. (implies import data types)";
"Import function information defined in the DWARF debug info\n" +
"(implies 'Import Data Types' is selected).";
private static final String OPTION_IMPORT_LIMIT_DIE_COUNT = "Debug item count limit";
private static final String OPTION_IMPORT_LIMIT_DIE_COUNT = "Debug Item Limit";
private static final String OPTION_IMPORT_LIMIT_DIE_COUNT_DESC =
"If the number of DWARF debug items are greater than this setting, DWARF analysis will be skipped.";
"If the number of DWARF debug items are greater than this setting, DWARF analysis will " +
"be skipped.";
private static final String OPTION_OUTPUT_SOURCE_INFO = "Output Source info";
private static final String OPTION_OUTPUT_SOURCE_INFO = "Output Source Info";
private static final String OPTION_OUTPUT_SOURCE_INFO_DESC =
"Include source code location info (filename:linenumber) in comments attached to the Ghidra datatype or function or variable created.";
"Include source code location info (filename:linenumber) in comments attached to the " +
"Ghidra datatype or function or variable created.";
private static final String OPTION_OUTPUT_DWARF_DIE_INFO = "Output DWARF DIE info";
private static final String OPTION_OUTPUT_DWARF_DIE_INFO = "Output DWARF DIE Info";
private static final String OPTION_OUTPUT_DWARF_DIE_INFO_DESC =
"Include DWARF DIE offset info in comments attached to the Ghidra datatype or function or variable created.";
"Include DWARF DIE offset info in comments attached to the Ghidra datatype or function " +
"or variable created.";
private static final String OPTION_NAME_LENGTH_CUTOFF = "Name length cutoff";
private static final String OPTION_NAME_LENGTH_CUTOFF = "Maximum Name Length";
private static final String OPTION_NAME_LENGTH_CUTOFF_DESC =
"Truncate symbol and type names longer than this limit. Range 20..2000";
private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS = "Lexical block comments";
private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS = "Add Lexical Block Comments";
private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS_DESC =
"Add comments to the start of lexical blocks";
private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS = "Inlined functions comments";
private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS =
"Add Inlined Functions Comments";
private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS_DESC =
"Add comments to the start of inlined functions";
private static final String OPTION_OUTPUT_FUNC_SIGS = "Output function signatures";
private static final String OPTION_OUTPUT_FUNC_SIGS = "Create Function Signatures";
private static final String OPTION_OUTPUT_FUNC_SIGS_DESC =
"Create function signature data types for each function encountered in the DWARF debug data.";
"Create function signature data types for each function encountered in the DWARF debug " +
"data.";
private static final String DWARF_ANALYZER_NAME = "DWARF";
private static final String DWARF_ANALYZER_DESCRIPTION =
"Automatically extracts DWARF info from an ELF file.";
//==================================================================================================
// Old Option Names - Should stick around for multiple major versions after 10.2
//==================================================================================================
private static final String OPTION_IMPORT_DATATYPES_OLD = "Import data types";
private static final String OPTION_PRELOAD_ALL_DIES_OLD = "Preload all DIEs";
private static final String OPTION_IMPORT_FUNCS_OLD = "Import functions";
private static final String OPTION_IMPORT_LIMIT_DIE_COUNT_OLD = "Debug item count limit";
private static final String OPTION_OUTPUT_SOURCE_INFO_OLD = "Output Source info";
private static final String OPTION_OUTPUT_DWARF_DIE_INFO_OLD = "Output DWARF DIE info";
private static final String OPTION_NAME_LENGTH_CUTOFF_OLD = "Name length cutoff";
private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS_OLD = "Lexical block comments";
private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS_OLD =
"Inlined functions comments";
private static final String OPTION_OUTPUT_FUNC_SIGS_OLD = "Output function signatures";
private AnalysisOptionsUpdater optionsUpdater = new AnalysisOptionsUpdater();
//==================================================================================================
// End Old Option Names
//==================================================================================================
/**
* Returns true if DWARF has already been imported into the specified program.
*
@@ -87,8 +116,8 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
* @return true if DWARF has already been imported, false if not yet
*/
public static boolean isAlreadyImported(Program program) {
Options propList = program.getOptions(Program.PROGRAM_INFO);
return propList.getBoolean(DWARF_LOADED_OPTION_NAME, false) ||
Options options = program.getOptions(Program.PROGRAM_INFO);
return options.getBoolean(DWARF_LOADED_OPTION_NAME, false) ||
oldCheckIfDWARFImported(program);
}
@@ -100,6 +129,26 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
setDefaultEnablement(true);
setPriority(AnalysisPriority.FORMAT_ANALYSIS.after());
setSupportsOneTimeAnalysis();
optionsUpdater.registerReplacement(OPTION_IMPORT_DATATYPES,
OPTION_IMPORT_DATATYPES_OLD);
optionsUpdater.registerReplacement(OPTION_PRELOAD_ALL_DIES,
OPTION_PRELOAD_ALL_DIES_OLD);
optionsUpdater.registerReplacement(OPTION_IMPORT_FUNCS,
OPTION_IMPORT_FUNCS_OLD);
optionsUpdater.registerReplacement(OPTION_IMPORT_LIMIT_DIE_COUNT,
OPTION_IMPORT_LIMIT_DIE_COUNT_OLD);
optionsUpdater.registerReplacement(OPTION_OUTPUT_SOURCE_INFO,
OPTION_OUTPUT_SOURCE_INFO_OLD);
optionsUpdater.registerReplacement(OPTION_OUTPUT_DWARF_DIE_INFO,
OPTION_OUTPUT_DWARF_DIE_INFO_OLD);
optionsUpdater.registerReplacement(OPTION_NAME_LENGTH_CUTOFF,
OPTION_NAME_LENGTH_CUTOFF_OLD);
optionsUpdater.registerReplacement(OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS,
OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS_OLD);
optionsUpdater.registerReplacement(OPTION_OUTPUT_INLINE_FUNC_COMMENTS,
OPTION_OUTPUT_INLINE_FUNC_COMMENTS_OLD);
optionsUpdater.registerReplacement(OPTION_OUTPUT_FUNC_SIGS, OPTION_OUTPUT_FUNC_SIGS_OLD);
}
@Override
@@ -185,6 +234,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_IMPORT_DATATYPES, importOptions.isImportDataTypes(), null,
OPTION_IMPORT_DATATYPES_DESC);
@@ -218,6 +268,11 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
null, OPTION_OUTPUT_FUNC_SIGS_DESC);
}
@Override
public AnalysisOptionsUpdater getOptionsUpdater() {
return optionsUpdater;
}
@Override
public void optionsChanged(Options options, Program program) {
importOptions.setOutputDIEInfo(
@@ -15,6 +15,7 @@
*/
package ghidra.app.services;
import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
@@ -24,12 +25,13 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* NOTE: ALL ANALYZER CLASSES MUST END IN "Analyzer". If not,
* the ClassSearcher will not find them.
*
* Interface to perform automatic analysis.
*
* NOTE: ALL ANALYZER CLASSES MUST END IN "Analyzer". If not, the ClassSearcher will not find
* them.
*/
public interface Analyzer extends ExtensionPoint {
/**
* Get the name of this analyzer
* @return analyzer name
@@ -43,15 +45,18 @@ public interface Analyzer extends ExtensionPoint {
public AnalyzerType getAnalysisType();
/**
* Returns true if this analyzer should be enabled by default. Generally useful
* analyzers should return true. Specialized analyzers should return false;
* Returns true if this analyzer should be enabled by default. Generally useful analyzers
* should return true. Specialized analyzers should return false;
* @param program the program
* @return true if enabled by default
*/
public boolean getDefaultEnablement(Program program);
/**
* Returns true if it makes sense for this analyzer to directly invoked on an address or
* addressSet. The AutoAnalyzer plug-in will automatically create an action for each
* analyzer that returns true.
* addressSet. The AutoAnalyzer plug-in will automatically create an action for each analyzer
* that returns true.
* @return true if supports one-time analysis
*/
public boolean supportsOneTimeAnalysis();
@@ -75,29 +80,31 @@ public interface Analyzer extends ExtensionPoint {
public boolean canAnalyze(Program program);
/**
* Called when the requested information type has been added.
* (ie: function added.)
* Called when the requested information type has been added, for example, when a function is
* added.
*
* @param program program to analyze
* @param set AddressSet of locations that have been added
* @param monitor monitor that indicates progress and indicates whether
* the user canceled the analysis
* @param monitor monitor that indicates progress and indicates whether the user canceled the
* analysis
* @param log a message log to record analysis information
* @return true if the analysis succeeded
* @throws CancelledException if the analysis is cancelled
*/
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException;
/**
* Called when the requested information type has been removed.
* (ie: function removed.)
* Called when the requested information type has been removed, for example, when a function is
* removed.
*
* @param program program to analyze
* @param set AddressSet of locations that have been added
* @param monitor monitor that indicates progress and indicates whether
* the user canceled the analysis
* @param monitor monitor that indicates progress and indicates whether the user canceled the
* analysis
* @param log a message log to record analysis information
* @return true if the analysis succeeded
* @throws CancelledException if the analysis is cancelled
*/
public boolean removed(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException;
@@ -111,8 +118,18 @@ public interface Analyzer extends ExtensionPoint {
public void registerOptions(Options options, Program program);
/**
* Analyzers should initialize their options from the values in the given Options,
* providing appropriate default values.
* Returns an optional options updater that allows clients to migrate old options to new
* options. This can be used to facilitate option name changes, as well as option value type
* changes.
* @return the updater; null if no updater
*/
public default AnalysisOptionsUpdater getOptionsUpdater() {
return null; // stub; clients will override as needed
}
/**
* Analyzers should initialize their options from the values in the given Options, providing
* appropriate default values.
* @param options the program options/property list that contains the options
* @param program program to be analyzed
*/
@@ -72,7 +72,6 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
@After
public void tearDown() throws Exception {
env.release(program);
env.dispose();
cleanUpStoredPreferences();
}
@@ -258,6 +257,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
//==================================================================================================
// Private Methods
//==================================================================================================
private void createConfig(String name, boolean stackOn, boolean refOn, boolean stringOn) {
setAnalyzerEnabled("Stack", stackOn);
setAnalyzerEnabled("Reference", refOn);
@@ -15,6 +15,7 @@
*/
package ghidra.feature.fid.analyzer;
import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
@@ -42,7 +43,8 @@ public class FidAnalyzer extends AbstractAnalyzer {
private static final String OPTION_DESCRIPTION_CREATE_BOOKMARKS =
"If checked, an analysis bookmark will be created for each function which was matched " +
"against one or more known library functions.";
public static final String APPLY_ALL_FID_LABELS_OPTION_NAME = "Always apply FID labels";
public static final String APPLY_ALL_FID_LABELS_OPTION_NAME = "Always Apply FID Labels";
private static final String APPLY_ALL_FID_LABELS_OPTION_DESCRIPTION = "Enable this option to " +
"always apply FID labels at functions regardless of existing labels at that function." +
" When enabled, FID labels will always be added." +
@@ -57,18 +59,33 @@ public class FidAnalyzer extends AbstractAnalyzer {
private boolean alwaysApplyFidLabels = APPLY_ALL_FID_LABELS_DEFAULT;
private boolean createBookmarksEnabled = OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED;
public static final String SCORE_THRESHOLD_OPTION_NAME = "Instruction count threshold";
private static final String SCORE_THRESHOLD_OPTION_NAME = "Instruction Count Threshold";
private static final String SCORE_THRESHOLD_OPTION_DESCRIPTION =
"The minimum score that a potential match must meet to be labeled by the analyzer. " +
"Score corresponds roughly to the number of instructions in the function.";
private float scoreThreshold;
public static final String MULTIMATCH_THRESHOLD_OPTION_NAME = "Multiple match threshold";
private static final String MULTIMATCH_THRESHOLD_OPTION_NAME = "Multiple Match Threshold";
private static final String MULTIMATCH_THRESHOLD_OPTION_DESCRIPTION =
"If there are multiple conflicting matches for a function, its score must exceed " +
"this secondary threshold in order to be labeled by the analyzer";
private float multiScoreThreshold;
//==================================================================================================
// Old Option Names - Should stick around for multiple major versions after 10.2
//==================================================================================================
private static final String SCORE_THRESHOLD_OPTION_NAME_OLD = "Instruction count threshold";
private static final String MULTIMATCH_THRESHOLD_OPTION_NAME_OLD = "Multiple match threshold";
private static final String APPLY_ALL_FID_LABELS_OPTION_NAME_OLD = "Always apply FID labels";
private AnalysisOptionsUpdater optionsUpdater = new AnalysisOptionsUpdater();
//==================================================================================================
// End Old Option Names
//==================================================================================================
public FidAnalyzer() {
/*
* FID is listed as a byte analyzer because we don't want to run it all the time. It
@@ -86,6 +103,13 @@ public class FidAnalyzer extends AbstractAnalyzer {
setPriority(AnalysisPriority.FUNCTION_ID_ANALYSIS.before());
scoreThreshold = service.getDefaultScoreThreshold();
multiScoreThreshold = service.getDefaultMultiNameThreshold();
optionsUpdater.registerReplacement(SCORE_THRESHOLD_OPTION_NAME,
SCORE_THRESHOLD_OPTION_NAME_OLD);
optionsUpdater.registerReplacement(MULTIMATCH_THRESHOLD_OPTION_NAME,
MULTIMATCH_THRESHOLD_OPTION_NAME_OLD);
optionsUpdater.registerReplacement(APPLY_ALL_FID_LABELS_OPTION_NAME,
APPLY_ALL_FID_LABELS_OPTION_NAME_OLD);
}
@Override
@@ -112,8 +136,9 @@ public class FidAnalyzer extends AbstractAnalyzer {
// Name Change can change the nature of a function from a system
// library. Probably a better way to do this.
AutoAnalysisManager.getAnalysisManager(program).functionModifierChanged(
cmd.getFIDLocations());
AutoAnalysisManager.getAnalysisManager(program)
.functionModifierChanged(
cmd.getFIDLocations());
return true;
}
@@ -129,6 +154,11 @@ public class FidAnalyzer extends AbstractAnalyzer {
OPTION_DESCRIPTION_CREATE_BOOKMARKS);
}
@Override
public AnalysisOptionsUpdater getOptionsUpdater() {
return optionsUpdater;
}
@Override
public void optionsChanged(Options options, Program program) {
scoreThreshold =
@@ -81,4 +81,8 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
int maxHeight = Math.max(dimension.height, component.getPreferredSize().height);
return new Dimension(dimension.width, maxHeight);
}
public String getLabelText() {
return label.getText();
}
}
@@ -35,7 +35,7 @@ public interface Options {
*
* @return String
*/
public abstract String getName();
public String getName();
/**
* Returns a unique id for option in this options with the given name. This will be the full
@@ -89,13 +89,13 @@ public interface Options {
* Set the location for where help can be found for this entire options object.
* @param helpLocation location for help on the option
*/
public abstract void setOptionsHelpLocation(HelpLocation helpLocation);
public void setOptionsHelpLocation(HelpLocation helpLocation);
/**
* Returns the HelpLocation for this entire Options object.
* @return the HelpLocation for this entire Options object.
*/
public abstract HelpLocation getOptionsHelpLocation();
public HelpLocation getOptionsHelpLocation();
/**
* Get the location for where help can be found for the option with
@@ -103,7 +103,7 @@ public interface Options {
* @param optionName name of the option
* @return null if the help location was not set on the option
*/
public abstract HelpLocation getHelpLocation(String optionName);
public HelpLocation getHelpLocation(String optionName);
/**
* Registers an option with a description, help location, and a default value without specifying
@@ -117,7 +117,7 @@ public interface Options {
* @param description a description of the option.
* @throws IllegalArgumentException if the defaultValue is null
*/
public abstract void registerOption(String optionName, Object defaultValue, HelpLocation help,
public void registerOption(String optionName, Object defaultValue, HelpLocation help,
String description);
/**
@@ -130,7 +130,7 @@ public interface Options {
* @param help the HelpLocation for this option.
* @param description a description of the option.
*/
public abstract void registerOption(String optionName, OptionType type, Object defaultValue,
public void registerOption(String optionName, OptionType type, Object defaultValue,
HelpLocation help, String description);
/**
@@ -146,20 +146,20 @@ public interface Options {
* then the property editor can't be null;
* @throws IllegalStateException if the options is a custom option and the editor is null.
*/
public abstract void registerOption(String optionName, OptionType type, Object defaultValue,
public void registerOption(String optionName, OptionType type, Object defaultValue,
HelpLocation help, String description, PropertyEditor editor);
/**
* Register the options editor that will handle the editing for all the options or a sub group of options.
* @param editor the custom editor panel to be used to edit the options or sub group of options.
*/
public abstract void registerOptionsEditor(OptionsEditor editor);
public void registerOptionsEditor(OptionsEditor editor);
/**
* Get the editor that will handle editing all the values in this options or sub group of options.
* @return null if no options editor was registered
*/
public abstract OptionsEditor getOptionsEditor();
public OptionsEditor getOptionsEditor();
/**
* Put the object value. If the option exists, the type must match the type of the existing
@@ -169,7 +169,7 @@ public interface Options {
* @throws IllegalStateException if the object does not match the existing type of the option.
* @throws IllegalArgumentException if the object is null or not a supported type.
*/
public abstract void putObject(String optionName, Object obj);
public void putObject(String optionName, Object obj);
/**
* Get the object value; called when the options dialog is being
@@ -179,7 +179,7 @@ public interface Options {
* @return object with the given option name; if no option was found,
* return default value (this value is not stored in the option maps)
*/
public abstract Object getObject(String optionName, Object defaultValue);
public Object getObject(String optionName, Object defaultValue);
/**
* Get the boolean value for the given option name.
@@ -188,7 +188,7 @@ public interface Options {
* is no option with the given name.
* @return boolean option value
*/
public abstract boolean getBoolean(String optionName, boolean defaultValue);
public boolean getBoolean(String optionName, boolean defaultValue);
/**
* Get the byte array for the given option name.
@@ -197,7 +197,7 @@ public interface Options {
* is no option with the given name
* @return byte[] byte array value
*/
public abstract byte[] getByteArray(String optionName, byte[] defaultValue);
public byte[] getByteArray(String optionName, byte[] defaultValue);
/**
* Get the int value for the given option name.
@@ -206,7 +206,7 @@ public interface Options {
* is no option with the given name
* @return int option value
*/
public abstract int getInt(String optionName, int defaultValue);
public int getInt(String optionName, int defaultValue);
/**
* Get the double value for the given option name.
@@ -215,7 +215,7 @@ public interface Options {
* is no option with the given name
* @return double value for the option
*/
public abstract double getDouble(String optionName, double defaultValue);
public double getDouble(String optionName, double defaultValue);
/**
* Get the float value for the given option name.
@@ -224,7 +224,7 @@ public interface Options {
* is no option with the given name
* @return float value for the option
*/
public abstract float getFloat(String optionName, float defaultValue);
public float getFloat(String optionName, float defaultValue);
/**
* Get the long value for the given option name.
@@ -233,7 +233,7 @@ public interface Options {
* is no option with the given name
* @return long value for the option
*/
public abstract long getLong(String optionName, long defaultValue);
public long getLong(String optionName, long defaultValue);
/**
* Get the custom option value for the given option name.
@@ -242,7 +242,7 @@ public interface Options {
* is no option with the given name
* @return WrappedOption value for the option
*/
public abstract CustomOption getCustomOption(String optionName, CustomOption defaultValue);
public CustomOption getCustomOption(String optionName, CustomOption defaultValue);
/**
* Get the Color for the given option name.
@@ -253,7 +253,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a Color
*/
public abstract Color getColor(String optionName, Color defaultValue);
public Color getColor(String optionName, Color defaultValue);
/**
* Get the File for the given option name.
@@ -264,7 +264,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a File options
*/
public abstract File getFile(String optionName, File defaultValue);
public File getFile(String optionName, File defaultValue);
/**
* Get the Date for the given option name.
@@ -275,7 +275,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a Date options
*/
public abstract Date getDate(String pName, Date date);
public Date getDate(String pName, Date date);
/**
* Get the Font for the given option name.
@@ -286,7 +286,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a Font
*/
public abstract Font getFont(String optionName, Font defaultValue);
public Font getFont(String optionName, Font defaultValue);
/**
* Get the KeyStrokg for the given action name.
@@ -297,7 +297,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a KeyStroke
*/
public abstract KeyStroke getKeyStroke(String optionName, KeyStroke defaultValue);
public KeyStroke getKeyStroke(String optionName, KeyStroke defaultValue);
/**
* Get the string value for the given option name.
@@ -306,7 +306,7 @@ public interface Options {
* option with the given name
* @return String value for the option
*/
public abstract String getString(String optionName, String defaultValue);
public String getString(String optionName, String defaultValue);
/**
* Get the Enum value for the given option name.
@@ -315,70 +315,70 @@ public interface Options {
* no option with the given name
* @return Enum value for the option
*/
public abstract <T extends Enum<T>> T getEnum(String optionName, T defaultValue);
public <T extends Enum<T>> T getEnum(String optionName, T defaultValue);
/**
* Sets the long value for the option.
* @param optionName name of the option
* @param value value of the option
*/
public abstract void setLong(String optionName, long value);
public void setLong(String optionName, long value);
/**
* Sets the boolean value for the option.
* @param optionName name of the option
* @param value value of the option
*/
public abstract void setBoolean(String optionName, boolean value);
public void setBoolean(String optionName, boolean value);
/**
* Sets the int value for the option.
* @param optionName name of the option
* @param value value of the option
*/
public abstract void setInt(String optionName, int value);
public void setInt(String optionName, int value);
/**
* Sets the double value for the option.
* @param optionName name of the option
* @param value value of the option
*/
public abstract void setDouble(String optionName, double value);
public void setDouble(String optionName, double value);
/**
* Sets the float value for the option.
* @param optionName name of the option
* @param value value of the option
*/
public abstract void setFloat(String optionName, float value);
public void setFloat(String optionName, float value);
/**
* Sets the Custom option value for the option.
* @param optionName name of the option
* @param value the value
*/
public abstract void setCustomOption(String optionName, CustomOption value);
public void setCustomOption(String optionName, CustomOption value);
/**
* Sets the byte[] value for the given option name.
* @param optionName the name of the option on which to save bytes.
* @param value the value
*/
public abstract void setByteArray(String optionName, byte[] value);
public void setByteArray(String optionName, byte[] value);
/**
* Sets the File value for the option.
* @param optionName name of the option
* @param value the value
*/
public abstract void setFile(String optionName, File value);
public void setFile(String optionName, File value);
/**
* Sets the Date value for the option.
* @param optionName name of the option
* @param newSetting the Date to set
*/
public abstract void setDate(String optionName, Date newSetting);
public void setDate(String optionName, Date newSetting);
/**
* Sets the Color value for the option
@@ -387,7 +387,7 @@ public interface Options {
* @throws IllegalArgumentException if a option with the given
* name already exists, but it is not a Color
*/
public abstract void setColor(String optionName, Color value);
public void setColor(String optionName, Color value);
/**
* Sets the Font value for the option
@@ -396,7 +396,7 @@ public interface Options {
* @throws IllegalArgumentException if a option with the given
* name already exists, but it is not a Font
*/
public abstract void setFont(String optionName, Font value);
public void setFont(String optionName, Font value);
/**
* Sets the KeyStroke value for the option
@@ -405,27 +405,27 @@ public interface Options {
* @throws IllegalArgumentException if a option with the given
* name already exists, but it is not a KeyStroke
*/
public abstract void setKeyStroke(String optionName, KeyStroke value);
public void setKeyStroke(String optionName, KeyStroke value);
/**
* Set the String value for the option.
* @param optionName name of the option
* @param value value of the option
*/
public abstract void setString(String optionName, String value);
public void setString(String optionName, String value);
/**
* Set the Enum value for the option.
* @param optionName name of the option
* @param value Enum value of the option
*/
public abstract <T extends Enum<T>> void setEnum(String optionName, T value);
public <T extends Enum<T>> void setEnum(String optionName, T value);
/**
* Remove the option name.
* @param optionName name of option to remove
*/
public abstract void removeOption(String optionName);
public void removeOption(String optionName);
/**
* Get the list of option names. This method will return the names (paths) of all options contained
@@ -434,21 +434,21 @@ public interface Options {
* the "aaa" and "bbb" names.
* @return the list of all option names(paths) under this options.
*/
public abstract List<String> getOptionNames();
public List<String> getOptionNames();
/**
* Return true if a option exists with the given name.
* @param optionName option name
* @return true if there exists an option with the given name
*/
public abstract boolean contains(String optionName);
public boolean contains(String optionName);
/**
* Get the description for the given option name.
* @param optionName name of the option
* @return null if the description or option name does not exist
*/
public abstract String getDescription(String optionName);
public String getDescription(String optionName);
/**
* Returns true if the specified option has been registered. Only registered names
@@ -456,21 +456,21 @@ public interface Options {
* @param optionName the option name
* @return true if registered
*/
public abstract boolean isRegistered(String optionName);
public boolean isRegistered(String optionName);
/**
* Returns true if the option with the given name's current value is the default value.
* @param optionName the name of the option.
* @return true if the options has its current value equal to its default value.
*/
public abstract boolean isDefaultValue(String optionName);
public boolean isDefaultValue(String optionName);
/**
* Restores <b>all</b> options contained herein to their default values.
*
* @see #restoreDefaultValue(String)
*/
public abstract void restoreDefaultValues();
public void restoreDefaultValues();
/**
* Restores the option denoted by the given name to its default value.
@@ -478,7 +478,7 @@ public interface Options {
* @param optionName The name of the option to restore
* @see #restoreDefaultValues()
*/
public abstract void restoreDefaultValue(String optionName);
public void restoreDefaultValue(String optionName);
/**
* Returns a Options object that is a sub-options of this options.
@@ -520,14 +520,14 @@ public interface Options {
* @param name the name of the option for which to retrieve the value as a string
* @return the value as a string for the given option.
*/
public abstract String getValueAsString(String name);
public String getValueAsString(String name);
/**
* Returns the default value as a string for the given option.
* @param optionName the name of the option for which to retrieve the default value as a string
* @return the default value as a string for the given option.
*/
public abstract String getDefaultValueAsString(String optionName);
public String getDefaultValueAsString(String optionName);
/**
* Returns true if the two options objects have the same set of options and values
@@ -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,6 +15,8 @@
*/
package ghidra.util.constraint;
import java.util.Objects;
import generic.constraint.ConstraintData;
import ghidra.program.model.listing.Program;
import ghidra.util.SystemUtilities;
@@ -26,8 +27,8 @@ public class PropertyConstraint extends ProgramConstraint {
super("property");
}
private String name; // name of the program property to constrain
private String value; // value the property should take
private String name; // name of the program property to constrain
private String value; // value the property should take
@Override
public boolean isSatisfied(Program program) {
@@ -41,6 +42,11 @@ public class PropertyConstraint extends ProgramConstraint {
value = data.getString("value");
}
@Override
public int hashCode() {
return Objects.hash(name, value);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PropertyConstraint)) {
@@ -54,5 +60,4 @@ public class PropertyConstraint extends ProgramConstraint {
public String getDescription() {
return "property " + name + " = " + value;
}
}