mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-4564: Improvements to library search paths and other loader options
This commit is contained in:
parent
4302fdc00d
commit
d3d60ea399
19 changed files with 466 additions and 376 deletions
|
@ -89,7 +89,7 @@ public class ScriptPathsPropertyEditor extends AbstractTypedPropertyEditor<Strin
|
||||||
protected class ScriptPathsPanel extends PathnameTablePanel {
|
protected class ScriptPathsPanel extends PathnameTablePanel {
|
||||||
public ScriptPathsPanel(String[] paths, Callback resetCallback) {
|
public ScriptPathsPanel(String[] paths, Callback resetCallback) {
|
||||||
// disable edits, top/bottom irrelevant, unordered
|
// disable edits, top/bottom irrelevant, unordered
|
||||||
super(paths, resetCallback, false, false, false);
|
super(paths, resetCallback, false, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -62,6 +62,7 @@ The Headless Analyzer can be useful when performing repetitive tasks on a projec
|
||||||
<BR> [-p]
|
<BR> [-p]
|
||||||
<BR> [-commit ["<comment>"]]
|
<BR> [-commit ["<comment>"]]
|
||||||
<BR> [-max-cpu <max cpu cores to use>]
|
<BR> [-max-cpu <max cpu cores to use>]
|
||||||
|
<BR> [-librarySearchPaths <path1>[;<path2>...]]
|
||||||
<BR> [-loader <desired loader name>]
|
<BR> [-loader <desired loader name>]
|
||||||
</FONT>
|
</FONT>
|
||||||
</P>
|
</P>
|
||||||
|
|
|
@ -312,17 +312,7 @@
|
||||||
empty, the folder that the main program is being imported to will be searched.</P>
|
empty, the folder that the main program is being imported to will be searched.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H4>Load Local Libraries From Disk</H4>
|
<H4>Load Libraries From Disk</H4>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P>Searches the executable's directory to recursively resolve the external libraries used
|
|
||||||
by the executable. The entire library dependency tree will be traversed in a depth-first
|
|
||||||
manner and a program will be created for each found library (if it doesn't exist already).
|
|
||||||
The <A href="help/topics/ReferencesPlugin/References_from.htm#extRefs">external references</A>
|
|
||||||
in these programs will be resolved.<BR>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<H4>Load System Libraries From Disk</H4>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>Searches a user-defined path list to recursively resolve the external libraries used
|
<P>Searches a user-defined path list to recursively resolve the external libraries used
|
||||||
|
@ -696,8 +686,12 @@
|
||||||
<H2><A name="Library_Paths"></A>Library Search Path</H2>
|
<H2><A name="Library_Paths"></A>Library Search Path</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The Library Search Path dialog is used to specify the directories that Ghidra should use
|
<P>The Library Search Path dialog is used to specify the directories, container files,
|
||||||
to resolve external libraries (e.g.; *.dll, *.so) while importing.</P>
|
and/or FSRLs that Ghidra should use to resolve external libraries (e.g.; *.dll, *.so) while
|
||||||
|
importing. A "." can be added to specify the the program's import location. FSRLs can be
|
||||||
|
added via the
|
||||||
|
<A href="help/topics/FileSystemBrowserPlugin/FileSystemBrowserPlugin.html#FSB_Add_Library_Search_Path">File System Browser context menu</A>.
|
||||||
|
</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<P align="center"><IMG alt="" src="images/SearchPathsDialog.png"></P>
|
<P align="center"><IMG alt="" src="images/SearchPathsDialog.png"></P>
|
||||||
|
@ -734,7 +728,8 @@
|
||||||
<OL>
|
<OL>
|
||||||
<LI>Click the <IMG alt="" src="images/Plus.png"> button</LI>
|
<LI>Click the <IMG alt="" src="images/Plus.png"> button</LI>
|
||||||
|
|
||||||
<LI>Select a directory from the file chooser</LI>
|
<LI>Select a directory or container file from the file chooser, or the program's
|
||||||
|
import location if "." is not present in the list already</LI>
|
||||||
|
|
||||||
<LI>Click the "Select Directory" button</LI>
|
<LI>Click the "Select Directory" button</LI>
|
||||||
</OL>
|
</OL>
|
||||||
|
|
|
@ -17,6 +17,13 @@ package ghidra.app.util;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
|
|
||||||
|
@ -28,6 +35,7 @@ public class Option {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Class<?> valueClass;
|
private final Class<?> valueClass;
|
||||||
private final String commandLineArgument;
|
private final String commandLineArgument;
|
||||||
|
private final String stateKey;
|
||||||
|
|
||||||
private Object value;
|
private Object value;
|
||||||
private OptionListener listener;
|
private OptionListener listener;
|
||||||
|
@ -85,11 +93,27 @@ public class Option {
|
||||||
* @param group Name for group of options
|
* @param group Name for group of options
|
||||||
*/
|
*/
|
||||||
public Option(String name, Class<?> valueClass, Object value, String arg, String group) {
|
public Option(String name, Class<?> valueClass, Object value, String arg, String group) {
|
||||||
|
this(name, valueClass, value, arg, group, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new Option
|
||||||
|
*
|
||||||
|
* @param name name of the option
|
||||||
|
* @param valueClass class of the option's value
|
||||||
|
* @param value value of the option
|
||||||
|
* @param arg the option's command line argument
|
||||||
|
* @param group Name for group of options
|
||||||
|
* @param stateKey state key name
|
||||||
|
*/
|
||||||
|
public Option(String name, Class<?> valueClass, Object value, String arg, String group,
|
||||||
|
String stateKey) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.valueClass = valueClass;
|
this.valueClass = valueClass;
|
||||||
this.commandLineArgument = arg;
|
this.commandLineArgument = arg;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.stateKey = stateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOptionListener(OptionListener listener) {
|
public void setOptionListener(OptionListener listener) {
|
||||||
|
@ -110,29 +134,28 @@ public class Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the class of the value for this Option.
|
* {@return the class of the value for this option}
|
||||||
*/
|
*/
|
||||||
public Class<?> getValueClass() {
|
public Class<?> getValueClass() {
|
||||||
return valueClass;
|
return valueClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the group name for this option; may be null if group was
|
* {@return the group name for this option; may be null if group was not specified}
|
||||||
* not specified.
|
|
||||||
*/
|
*/
|
||||||
public String getGroup() {
|
public String getGroup() {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name of this Option.
|
* {@return the name of this option}
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the value of this Option.
|
* {@return the value of this option}
|
||||||
*/
|
*/
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return value;
|
return value;
|
||||||
|
@ -168,19 +191,12 @@ public class Option {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Boolean.class.isAssignableFrom(getValueClass())) {
|
if (Boolean.class.isAssignableFrom(getValueClass())) {
|
||||||
Boolean b = null;
|
try {
|
||||||
if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("t") ||
|
setValue(BooleanUtils.toBoolean(str, "true", "false"));
|
||||||
str.equalsIgnoreCase("yes") || str.equalsIgnoreCase("y")) {
|
|
||||||
b = true;
|
|
||||||
}
|
}
|
||||||
else if (str.equalsIgnoreCase("false") || str.equalsIgnoreCase("f") ||
|
catch (IllegalArgumentException e) {
|
||||||
str.equalsIgnoreCase("no") || str.equalsIgnoreCase("n")) {
|
|
||||||
b = false;
|
|
||||||
}
|
|
||||||
if (b == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setValue(b);
|
|
||||||
}
|
}
|
||||||
else if (HexLong.class.isAssignableFrom(getValueClass())) {
|
else if (HexLong.class.isAssignableFrom(getValueClass())) {
|
||||||
try {
|
try {
|
||||||
|
@ -231,18 +247,45 @@ public class Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the command line argument for this Option.
|
* {@return the command line argument for this option (could be null)}
|
||||||
*
|
|
||||||
* @return The command line argument for this Option. Could be null.
|
|
||||||
*/
|
*/
|
||||||
public String getArg() {
|
public String getArg() {
|
||||||
return commandLineArgument;
|
return commandLineArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the state key name (could be null)}
|
||||||
|
*/
|
||||||
|
public String getStateKey() {
|
||||||
|
return stateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the current project state associated with this option (could be null)}
|
||||||
|
*/
|
||||||
|
public SaveState getState() {
|
||||||
|
Project project = AppInfo.getActiveProject();
|
||||||
|
if (project == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final SaveState state;
|
||||||
|
SaveState existingState = stateKey != null ? project.getSaveableData(stateKey) : null;
|
||||||
|
if (existingState != null) {
|
||||||
|
state = existingState;
|
||||||
|
}
|
||||||
|
else if (stateKey != null) {
|
||||||
|
state = new SaveState();
|
||||||
|
project.setSaveableData(stateKey, state);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state = null;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Group:\"" + group + "\" Name:\"" + name + "\" Arg:\"" + commandLineArgument +
|
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||||
"\" Type:\"" + valueClass + "\" Value:\"" + value + "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -250,7 +293,7 @@ public class Option {
|
||||||
* @return a copy of this Option object.
|
* @return a copy of this Option object.
|
||||||
*/
|
*/
|
||||||
public Option copy() {
|
public Option copy() {
|
||||||
return new Option(name, valueClass, value, commandLineArgument, group);
|
return new Option(name, valueClass, value, commandLineArgument, group, stateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> getValueClass(Object v) {
|
private static Class<?> getValueClass(Object v) {
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util;
|
package ghidra.app.util;
|
||||||
|
|
||||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,17 +26,10 @@ import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
|
|
||||||
import docking.DockingWindowManager;
|
|
||||||
import docking.widgets.button.BrowseButton;
|
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
import ghidra.app.util.opinion.*;
|
|
||||||
import ghidra.framework.main.AppInfo;
|
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.Project;
|
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
@ -200,15 +191,6 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
*/
|
*/
|
||||||
private Component getEditorComponent(Option option) {
|
private Component getEditorComponent(Option option) {
|
||||||
|
|
||||||
// Special cases for library link/load options
|
|
||||||
if (option.getName().equals(AbstractLibrarySupportLoader.LINK_SEARCH_FOLDER_OPTION_NAME) ||
|
|
||||||
option.getName().equals(AbstractLibrarySupportLoader.LIBRARY_DEST_FOLDER_OPTION_NAME)) {
|
|
||||||
return buildProjectFolderEditor(option);
|
|
||||||
}
|
|
||||||
if (option.getName().equals(AbstractLibrarySupportLoader.SYSTEM_LIBRARY_OPTION_NAME)) {
|
|
||||||
return buildPathsEditor(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
Component customEditorComponent = option.getCustomEditorComponent();
|
Component customEditorComponent = option.getCustomEditorComponent();
|
||||||
if (customEditorComponent != null) {
|
if (customEditorComponent != null) {
|
||||||
return customEditorComponent;
|
return customEditorComponent;
|
||||||
|
@ -243,59 +225,6 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component buildPathsEditor(Option option) {
|
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
|
||||||
JButton button = new JButton("Edit Paths");
|
|
||||||
button.addActionListener(
|
|
||||||
e -> DockingWindowManager.showDialog(panel, new LibraryPathsDialog()));
|
|
||||||
Boolean value = (Boolean) option.getValue();
|
|
||||||
boolean initialState = value != null ? value : false;
|
|
||||||
GCheckBox jCheckBox = new GCheckBox("", initialState);
|
|
||||||
jCheckBox.addActionListener(e -> {
|
|
||||||
boolean b = jCheckBox.isSelected();
|
|
||||||
option.setValue(b);
|
|
||||||
});
|
|
||||||
panel.add(jCheckBox, BorderLayout.WEST);
|
|
||||||
panel.add(button, BorderLayout.EAST);
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component buildProjectFolderEditor(Option option) {
|
|
||||||
Project project = AppInfo.getActiveProject();
|
|
||||||
final SaveState state;
|
|
||||||
SaveState existingState = project.getSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY);
|
|
||||||
if (existingState != null) {
|
|
||||||
state = existingState;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
state = new SaveState();
|
|
||||||
project.setSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY, state);
|
|
||||||
}
|
|
||||||
String lastFolderPath = state.getString(option.getName(), "");
|
|
||||||
option.setValue(lastFolderPath);
|
|
||||||
JTextField textField = new JTextField(lastFolderPath);
|
|
||||||
textField.setEditable(false);
|
|
||||||
JButton button = new BrowseButton();
|
|
||||||
button.addActionListener(e -> {
|
|
||||||
DataTreeDialog dataTreeDialog =
|
|
||||||
new DataTreeDialog(this, "Choose a project folder", CHOOSE_FOLDER);
|
|
||||||
String folderPath = lastFolderPath.isBlank() ? "/" : lastFolderPath;
|
|
||||||
dataTreeDialog.setSelectedFolder(project.getProjectData().getFolder(folderPath));
|
|
||||||
dataTreeDialog.showComponent();
|
|
||||||
DomainFolder folder = dataTreeDialog.getDomainFolder();
|
|
||||||
if (folder != null) {
|
|
||||||
String newFolderPath = folder.getPathname();
|
|
||||||
textField.setText(newFolderPath);
|
|
||||||
option.setValue(newFolderPath);
|
|
||||||
state.putString(option.getName(), newFolderPath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
|
||||||
panel.add(textField, BorderLayout.CENTER);
|
|
||||||
panel.add(button, BorderLayout.EAST);
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component getAddressSpaceEditorComponent(Option option) {
|
private Component getAddressSpaceEditorComponent(Option option) {
|
||||||
if (addressFactoryService == null) {
|
if (addressFactoryService == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -320,49 +249,84 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component getStringEditorComponent(Option option) {
|
private Component getStringEditorComponent(Option option) {
|
||||||
|
final SaveState state = option.getState();
|
||||||
|
String defaultValue = (String) option.getValue();
|
||||||
|
String value =
|
||||||
|
state != null ? state.getString(option.getName(), defaultValue) : defaultValue;
|
||||||
|
option.setValue(value);
|
||||||
JTextField tf = new JTextField(5);
|
JTextField tf = new JTextField(5);
|
||||||
tf.setName(option.getName());
|
tf.setName(option.getName());
|
||||||
tf.getDocument().addDocumentListener(new ImporterDocumentListener(option, tf));
|
tf.getDocument().addDocumentListener(new ImporterDocumentListener(option, tf, state));
|
||||||
String value = option.getValue() == null ? "" : (String) option.getValue();
|
|
||||||
tf.setText(value);
|
tf.setText(value);
|
||||||
return tf;
|
return tf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component getHexLongEditorComponent(Option option) {
|
private Component getHexLongEditorComponent(Option option) {
|
||||||
|
final SaveState state = option.getState();
|
||||||
|
HexLong defaultValue = (HexLong) option.getValue();
|
||||||
|
long value = state != null ? state.getLong(option.getName(), defaultValue.longValue())
|
||||||
|
: defaultValue.longValue();
|
||||||
|
option.setValue(new HexLong(value));
|
||||||
IntegerTextField field = new IntegerTextField();
|
IntegerTextField field = new IntegerTextField();
|
||||||
HexLong hexLong = (HexLong) option.getValue();
|
|
||||||
long value = hexLong == null ? 0 : hexLong.longValue();
|
|
||||||
field.setValue(value);
|
field.setValue(value);
|
||||||
field.setHexMode();
|
field.setHexMode();
|
||||||
field.addChangeListener(e -> option.setValue(new HexLong(field.getLongValue())));
|
field.addChangeListener(e -> {
|
||||||
|
option.setValue(new HexLong(field.getLongValue()));
|
||||||
|
if (state != null) {
|
||||||
|
state.putLong(option.getName(), field.getLongValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
return field.getComponent();
|
return field.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component getIntegerEditorComponent(Option option) {
|
private Component getIntegerEditorComponent(Option option) {
|
||||||
|
final SaveState state = option.getState();
|
||||||
|
int defaultValue = (int) option.getValue();
|
||||||
|
int value = state != null ? state.getInt(option.getName(), defaultValue) : defaultValue;
|
||||||
|
option.setValue(value);
|
||||||
IntegerTextField field = new IntegerTextField();
|
IntegerTextField field = new IntegerTextField();
|
||||||
Integer value = (Integer) option.getValue();
|
|
||||||
if (value != null) {
|
|
||||||
field.setValue(value);
|
field.setValue(value);
|
||||||
|
field.addChangeListener(e -> {
|
||||||
|
option.setValue(field.getIntValue());
|
||||||
|
if (state != null) {
|
||||||
|
state.putInt(option.getName(), field.getIntValue());
|
||||||
}
|
}
|
||||||
field.addChangeListener(e -> option.setValue(field.getIntValue()));
|
});
|
||||||
return field.getComponent();
|
return field.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component getLongEditorComponent(Option option) {
|
private Component getLongEditorComponent(Option option) {
|
||||||
|
final SaveState state = option.getState();
|
||||||
|
long defaultValue = (long) option.getValue();
|
||||||
|
long value =
|
||||||
|
state != null ? state.getLong(option.getName(), defaultValue) : defaultValue;
|
||||||
|
option.setValue(value);
|
||||||
IntegerTextField field = new IntegerTextField();
|
IntegerTextField field = new IntegerTextField();
|
||||||
Long value = (Long) option.getValue();
|
|
||||||
field.setValue(value);
|
field.setValue(value);
|
||||||
field.addChangeListener(e -> option.setValue(field.getLongValue()));
|
field.addChangeListener(e -> {
|
||||||
|
option.setValue(field.getLongValue());
|
||||||
|
if (state != null) {
|
||||||
|
state.putLong(option.getName(), field.getLongValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
return field.getComponent();
|
return field.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component getBooleanEditorComponent(Option option) {
|
private Component getBooleanEditorComponent(Option option) {
|
||||||
|
final SaveState state = option.getState();
|
||||||
|
boolean defaultValue = (boolean) option.getValue();
|
||||||
|
boolean initialState =
|
||||||
|
state != null ? state.getBoolean(option.getName(), defaultValue) : defaultValue;
|
||||||
|
option.setValue(initialState);
|
||||||
GCheckBox cb = new GCheckBox();
|
GCheckBox cb = new GCheckBox();
|
||||||
cb.setName(option.getName());
|
cb.setName(option.getName());
|
||||||
Boolean b = (Boolean) option.getValue();
|
|
||||||
boolean initialState = b != null ? b : false;
|
|
||||||
cb.setSelected(initialState);
|
cb.setSelected(initialState);
|
||||||
cb.addItemListener(e -> option.setValue(cb.isSelected()));
|
cb.addItemListener(e -> {
|
||||||
|
option.setValue(cb.isSelected());
|
||||||
|
if (state != null) {
|
||||||
|
state.putBoolean(option.getName(), cb.isSelected());
|
||||||
|
}
|
||||||
|
});
|
||||||
return cb;
|
return cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,16 +347,17 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
addressInput.addChangeListener(e -> option.setValue(addressInput.getAddress()));// addressInput.addActionListener(e -> option.setValue(addressInput.getAddress()));
|
addressInput.addChangeListener(e -> option.setValue(addressInput.getAddress()));// addressInput.addActionListener(e -> option.setValue(addressInput.getAddress()));
|
||||||
return addressInput;
|
return addressInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImporterDocumentListener implements DocumentListener {
|
class ImporterDocumentListener implements DocumentListener {
|
||||||
private Option option;
|
private Option option;
|
||||||
private JTextField textField;
|
private JTextField textField;
|
||||||
|
private SaveState state;
|
||||||
|
|
||||||
ImporterDocumentListener(Option option, JTextField textField) {
|
ImporterDocumentListener(Option option, JTextField textField, SaveState state) {
|
||||||
this.option = option;
|
this.option = option;
|
||||||
this.textField = textField;
|
this.textField = textField;
|
||||||
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -413,5 +378,8 @@ class ImporterDocumentListener implements DocumentListener {
|
||||||
private void updated() {
|
private void updated() {
|
||||||
String text = textField.getText();
|
String text = textField.getText();
|
||||||
option.setValue(text);
|
option.setValue(text);
|
||||||
|
if (state != null) {
|
||||||
|
state.putString(option.getName(), text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.*;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
import ghidra.*;
|
import ghidra.*;
|
||||||
|
import ghidra.app.util.importer.LibrarySearchPathManager;
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.Loader;
|
||||||
import ghidra.framework.*;
|
import ghidra.framework.*;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
@ -341,6 +342,9 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
||||||
else if ("-okToDelete".equalsIgnoreCase(args[argi])) {
|
else if ("-okToDelete".equalsIgnoreCase(args[argi])) {
|
||||||
options.setOkToDelete(true);
|
options.setOkToDelete(true);
|
||||||
}
|
}
|
||||||
|
else if (checkArgument("-librarySearchPaths", args, argi)) {
|
||||||
|
LibrarySearchPathManager.setLibraryPaths(args[++argi].split(";"));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new InvalidInputException("Bad argument: " + arg);
|
throw new InvalidInputException("Bad argument: " + arg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@ package ghidra.app.util.importer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
|
@ -718,6 +717,8 @@ public final class AutoImporter {
|
||||||
Msg.info(AutoImporter.class, "Using Loader: " + loadSpec.getLoader().getName());
|
Msg.info(AutoImporter.class, "Using Loader: " + loadSpec.getLoader().getName());
|
||||||
Msg.info(AutoImporter.class,
|
Msg.info(AutoImporter.class,
|
||||||
"Using Language/Compiler: " + loadSpec.getLanguageCompilerSpec());
|
"Using Language/Compiler: " + loadSpec.getLanguageCompilerSpec());
|
||||||
|
Msg.info(AutoImporter.class, "Using Library Search Path: " +
|
||||||
|
Arrays.toString(LibrarySearchPathManager.getLibraryPaths()));
|
||||||
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
.load(provider, importName, project, projectFolderPath, loadSpec, loaderOptions,
|
.load(provider, importName, project, projectFolderPath, loadSpec, loaderOptions,
|
||||||
messageLog, consumer, monitor);
|
messageLog, consumer, monitor);
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* ###
|
||||||
|
* 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.util.importer;
|
||||||
|
|
||||||
|
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Component;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.opinion.Loader;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Option} used to specify a {@link DomainFolder}
|
||||||
|
*/
|
||||||
|
public class DomainFolderOption extends Option {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link DomainFolderOption}
|
||||||
|
*
|
||||||
|
* @param name The name of the option
|
||||||
|
* @param arg The option's command line argument (could be null)
|
||||||
|
*/
|
||||||
|
public DomainFolderOption(String name, String arg) {
|
||||||
|
super(name, String.class, "", arg, null, Loader.OPTIONS_PROJECT_SAVE_STATE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getCustomEditorComponent() {
|
||||||
|
final SaveState state = getState();
|
||||||
|
String defaultValue = (String) getValue();
|
||||||
|
String lastFolderPath =
|
||||||
|
state != null ? state.getString(getName(), defaultValue) : defaultValue;
|
||||||
|
setValue(lastFolderPath);
|
||||||
|
JTextField textField = new JTextField(lastFolderPath);
|
||||||
|
textField.setEditable(false);
|
||||||
|
JButton button = new BrowseButton();
|
||||||
|
button.addActionListener(e -> {
|
||||||
|
DataTreeDialog dataTreeDialog =
|
||||||
|
new DataTreeDialog(null, "Choose a project folder", CHOOSE_FOLDER);
|
||||||
|
String folderPath = lastFolderPath.isBlank() ? "/" : lastFolderPath;
|
||||||
|
dataTreeDialog.setSelectedFolder(
|
||||||
|
AppInfo.getActiveProject().getProjectData().getFolder(folderPath));
|
||||||
|
dataTreeDialog.showComponent();
|
||||||
|
DomainFolder folder = dataTreeDialog.getDomainFolder();
|
||||||
|
if (folder != null) {
|
||||||
|
String newFolderPath = folder.getPathname();
|
||||||
|
textField.setText(newFolderPath);
|
||||||
|
setValue(newFolderPath);
|
||||||
|
if (state != null) {
|
||||||
|
state.putString(getName(), newFolderPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.add(textField, BorderLayout.CENTER);
|
||||||
|
panel.add(button, BorderLayout.EAST);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getValueClass() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Option copy() {
|
||||||
|
return new DomainFolderOption(getName(), getArg());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* ###
|
||||||
|
* 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.util.importer;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
|
||||||
|
import docking.DockingWindowManager;
|
||||||
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.opinion.LibraryPathsDialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dummy {@link Option} used to render a button that will allow the user to edit the global
|
||||||
|
* list of library search paths
|
||||||
|
*/
|
||||||
|
public class LibrarySearchPathDummyOption extends Option {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link LibrarySearchPathDummyOption}
|
||||||
|
*
|
||||||
|
* @param name The name of the option
|
||||||
|
*/
|
||||||
|
public LibrarySearchPathDummyOption(String name) {
|
||||||
|
super(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getCustomEditorComponent() {
|
||||||
|
JButton button = new JButton("Edit Paths");
|
||||||
|
button.addActionListener(e -> {
|
||||||
|
DockingWindowManager.showDialog(null, new LibraryPathsDialog());
|
||||||
|
});
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getValueClass() {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Option copy() {
|
||||||
|
return new LibrarySearchPathDummyOption(getName());
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,71 +20,66 @@ import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.formats.gfilesystem.FileSystemService;
|
import ghidra.app.util.opinion.Loader;
|
||||||
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.framework.Platform;
|
import ghidra.framework.Platform;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple class for managing the library search path
|
* A simple class for managing the library search path and avoiding duplicate directories.
|
||||||
* and avoiding duplicate directories.
|
|
||||||
*/
|
*/
|
||||||
public class LibrarySearchPathManager {
|
public class LibrarySearchPathManager {
|
||||||
private static List<String> pathList = createPathList();
|
|
||||||
|
|
||||||
private static boolean hasBeenRestored;
|
private static final String LIBRARY_SEARCH_PATH_STATE_NAME = "Library Search Paths";
|
||||||
|
private static Set<String> pathSet = initialize();
|
||||||
private static List<String> createPathList() {
|
|
||||||
pathList = new ArrayList<>();
|
|
||||||
loadJavaLibraryPath();
|
|
||||||
return pathList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void loadJavaLibraryPath() {
|
|
||||||
List<String> paths = Platform.CURRENT_PLATFORM.getAdditionalLibraryPaths();
|
|
||||||
for (String path : paths) {
|
|
||||||
addPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
String libpath = System.getProperty("java.library.path");
|
|
||||||
String libpathSep = System.getProperty("path.separator");
|
|
||||||
|
|
||||||
StringTokenizer nizer = new StringTokenizer(libpath, libpathSep);
|
|
||||||
while (nizer.hasMoreTokens()) {
|
|
||||||
String path = nizer.nextToken();
|
|
||||||
addPath(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of directories to search for libraries
|
* Returns an array of library search paths
|
||||||
* @return an array of directories to search for libraries
|
*
|
||||||
|
* @return an array of library search paths
|
||||||
*/
|
*/
|
||||||
public static String[] getLibraryPaths() {
|
public static synchronized String[] getLibraryPaths() {
|
||||||
String[] paths = new String[pathList.size()];
|
String[] paths = new String[pathSet.size()];
|
||||||
pathList.toArray(paths);
|
pathSet.toArray(paths);
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link List} of {@link FSRL}s to search for libraries
|
* Returns a {@link List} of {@link FSRL}s to search for libraries
|
||||||
|
*
|
||||||
|
* @param provider The {@link ByteProvider} of the program being loaded
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancellable monitor
|
* @param monitor A cancellable monitor
|
||||||
* @return a {@link List} of {@link FSRL}s to search for libraries
|
* @return a {@link List} of {@link FSRL}s to search for libraries
|
||||||
* @throws CancelledException if the user cancelled the operation
|
* @throws CancelledException if the user cancelled the operation
|
||||||
*/
|
*/
|
||||||
public static List<FSRL> getLibraryFsrlList(MessageLog log, TaskMonitor monitor)
|
public static synchronized List<FSRL> getLibraryFsrlList(ByteProvider provider, MessageLog log,
|
||||||
throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
List<FSRL> fsrlList = new ArrayList<>();
|
List<FSRL> fsrlList = new ArrayList<>();
|
||||||
for (String path : pathList) {
|
for (String path : pathSet) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
path = path.trim();
|
path = path.trim();
|
||||||
FSRL fsrl = null;
|
FSRL fsrl = null;
|
||||||
try {
|
try {
|
||||||
|
if (path.equals(".")) {
|
||||||
|
FSRL providerFsrl = provider.getFSRL();
|
||||||
|
if (providerFsrl != null) {
|
||||||
|
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
|
||||||
|
GFile parentFile = fileRef.file.getParentFile();
|
||||||
|
fsrl = parentFile.getFSRL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
fsrl = FSRL.fromString(path);
|
fsrl = FSRL.fromString(path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (MalformedURLException e) {
|
catch (MalformedURLException e) {
|
||||||
try {
|
try {
|
||||||
File f = new File(path);
|
File f = new File(path);
|
||||||
|
@ -96,6 +91,9 @@ public class LibrarySearchPathManager {
|
||||||
log.appendException(e2);
|
log.appendException(e2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.appendException(e);
|
||||||
|
}
|
||||||
if (fsrl != null) {
|
if (fsrl != null) {
|
||||||
fsrlList.add(fsrl);
|
fsrlList.add(fsrl);
|
||||||
}
|
}
|
||||||
|
@ -104,85 +102,95 @@ public class LibrarySearchPathManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the directories to search for libraries
|
* Sets the library search paths to the given array
|
||||||
|
*
|
||||||
* @param paths the new library search paths
|
* @param paths the new library search paths
|
||||||
*/
|
*/
|
||||||
public static void setLibraryPaths(String[] paths) {
|
public static synchronized void setLibraryPaths(String[] paths) {
|
||||||
|
pathSet.clear();
|
||||||
pathList.clear();
|
pathSet.addAll(Arrays.asList(paths));
|
||||||
for (String path : paths) {
|
saveState();
|
||||||
addPath(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this to restore paths that were previously persisted. If you really need to change
|
* Adds the specified library search path path to the end of the path search list
|
||||||
* the paths <b>for the entire JVM</b>, then call {@link #setLibraryPaths(String[])}.
|
|
||||||
*
|
*
|
||||||
* @param paths the paths to restore
|
* @param path the library search path to add
|
||||||
*/
|
|
||||||
public static void restoreLibraryPaths(String[] paths) {
|
|
||||||
|
|
||||||
if (hasBeenRestored) {
|
|
||||||
//
|
|
||||||
// We code that restores paths from tool config files. It is a mistake to do this
|
|
||||||
// every time we load a tool, as the values can get out-of-sync if tools do not
|
|
||||||
// save properly. Logically, we only need to restore once.
|
|
||||||
//
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLibraryPaths(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the specified path to the end of the path search list.
|
|
||||||
* @param path the path to add
|
|
||||||
* @return true if the path was appended, false if the path was a duplicate
|
* @return true if the path was appended, false if the path was a duplicate
|
||||||
*/
|
*/
|
||||||
public static boolean addPath(String path) {
|
public static synchronized boolean addPath(String path) {
|
||||||
if (pathList.indexOf(path) == -1) {
|
if (pathSet.contains(path)) {
|
||||||
pathList.add(path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
pathSet.add(path);
|
||||||
/**
|
saveState();
|
||||||
* Adds the path at the specified index in path search list.
|
|
||||||
* @param index The index
|
|
||||||
* @param path the path to add
|
|
||||||
* @return true if the path was appended, false if the path was a duplicate
|
|
||||||
*/
|
|
||||||
public static boolean addPathAt(int index, String path) {
|
|
||||||
if (pathList.indexOf(path) == -1) {
|
|
||||||
pathList.add(index, path);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the path from the path search list.
|
* Resets the library search path to the default values
|
||||||
* @param path the path the remove
|
|
||||||
* @return true if the path was removed, false if the path did not exist
|
|
||||||
*/
|
*/
|
||||||
public static boolean removePath(String path) {
|
public static synchronized void reset() {
|
||||||
return pathList.remove(path);
|
pathSet = loadDefaultPaths();
|
||||||
|
saveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private LibrarySearchPathManager() {
|
||||||
* Resets the library search path to match the system search paths.
|
// Prevent instantiation of utility class
|
||||||
*/
|
|
||||||
public static void reset() {
|
|
||||||
pathList.clear();
|
|
||||||
loadJavaLibraryPath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static synchronized Set<String> initialize() {
|
||||||
* Clears all paths.
|
Set<String> set = loadFromSavedState();
|
||||||
*/
|
if (set == null) {
|
||||||
public static void clear() {
|
set = loadDefaultPaths();
|
||||||
pathList.clear();
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Set<String> loadDefaultPaths() {
|
||||||
|
Set<String> set = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
// Add program import location
|
||||||
|
set.add(".");
|
||||||
|
|
||||||
|
// Add platform specific locations
|
||||||
|
Platform.CURRENT_PLATFORM.getAdditionalLibraryPaths().forEach(p -> set.add(p));
|
||||||
|
|
||||||
|
// Add Java library path locations
|
||||||
|
String libpath = System.getProperty("java.library.path");
|
||||||
|
String libpathSep = System.getProperty("path.separator");
|
||||||
|
StringTokenizer nizer = new StringTokenizer(libpath, libpathSep);
|
||||||
|
while (nizer.hasMoreTokens()) {
|
||||||
|
String path = nizer.nextToken();
|
||||||
|
set.add(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Set<String> loadFromSavedState() {
|
||||||
|
Project project = AppInfo.getActiveProject();
|
||||||
|
if (project != null) {
|
||||||
|
SaveState saveState = project.getSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY);
|
||||||
|
if (saveState != null) {
|
||||||
|
String[] paths = saveState.getStrings(LIBRARY_SEARCH_PATH_STATE_NAME, null);
|
||||||
|
if (paths != null) {
|
||||||
|
return new LinkedHashSet<String>(Arrays.asList(paths));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void saveState() {
|
||||||
|
Project project = AppInfo.getActiveProject();
|
||||||
|
if (project != null) {
|
||||||
|
SaveState saveState = project.getSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY);
|
||||||
|
if (saveState == null) {
|
||||||
|
saveState = new SaveState();
|
||||||
|
project.setSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY, saveState);
|
||||||
|
}
|
||||||
|
saveState.putStrings(LIBRARY_SEARCH_PATH_STATE_NAME, pathSet.toArray(new String[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,7 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.OptionUtils;
|
import ghidra.app.util.OptionUtils;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.LibrarySearchPathManager;
|
import ghidra.app.util.importer.*;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -57,11 +56,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
public static final String LINK_SEARCH_FOLDER_OPTION_NAME = "Project Library Search Folder";
|
public static final String LINK_SEARCH_FOLDER_OPTION_NAME = "Project Library Search Folder";
|
||||||
static final String LINK_SEARCH_FOLDER_OPTION_DEFAULT = "";
|
static final String LINK_SEARCH_FOLDER_OPTION_DEFAULT = "";
|
||||||
|
|
||||||
public static final String LOCAL_LIBRARY_OPTION_NAME = "Load Local Libraries From Disk";
|
public static final String LOAD_LIBRARY_OPTION_NAME = "Load Libraries From Disk";
|
||||||
static final boolean LOCAL_LIBRARY_OPTION_DEFAULT = false;
|
static final boolean LOAD_LIBRARY_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
public static final String SYSTEM_LIBRARY_OPTION_NAME = "Load System Libraries From Disk";
|
public static final String LIBRARY_SEARCH_PATH_DUMMY_OPTION_NAME = "Library Search Paths";
|
||||||
static final boolean SYSTEM_LIBRARY_OPTION_DEFAULT = false;
|
|
||||||
|
|
||||||
public static final String DEPTH_OPTION_NAME = "Recursive Library Load Depth";
|
public static final String DEPTH_OPTION_NAME = "Recursive Library Load Depth";
|
||||||
static final int DEPTH_OPTION_DEFAULT = 1;
|
static final int DEPTH_OPTION_DEFAULT = 1;
|
||||||
|
@ -143,8 +141,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (loadedPrograms.isEmpty()) {
|
if (loadedPrograms.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isLinkExistingLibraries(options) || isLoadLocalLibraries(options) ||
|
if (isLinkExistingLibraries(options) || isLoadLibraries(options)) {
|
||||||
isLoadSystemLibraries(options)) {
|
|
||||||
String projectFolderPath = loadedPrograms.get(0).getProjectFolderPath();
|
String projectFolderPath = loadedPrograms.get(0).getProjectFolderPath();
|
||||||
List<DomainFolder> searchFolders = new ArrayList<>();
|
List<DomainFolder> searchFolders = new ArrayList<>();
|
||||||
String destPath = getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
String destPath = getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
||||||
|
@ -177,18 +174,19 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
DomainObject domainObject, boolean loadIntoProgram) {
|
DomainObject domainObject, boolean loadIntoProgram) {
|
||||||
List<Option> list =
|
List<Option> list =
|
||||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||||
|
|
||||||
list.add(new Option(LINK_EXISTING_OPTION_NAME, LINK_EXISTING_OPTION_DEFAULT, Boolean.class,
|
list.add(new Option(LINK_EXISTING_OPTION_NAME, LINK_EXISTING_OPTION_DEFAULT, Boolean.class,
|
||||||
Loader.COMMAND_LINE_ARG_PREFIX + "-linkExistingProjectLibraries"));
|
Loader.COMMAND_LINE_ARG_PREFIX + "-linkExistingProjectLibraries"));
|
||||||
list.add(new Option(LINK_SEARCH_FOLDER_OPTION_NAME, LINK_SEARCH_FOLDER_OPTION_DEFAULT,
|
list.add(new DomainFolderOption(LINK_SEARCH_FOLDER_OPTION_NAME,
|
||||||
String.class, Loader.COMMAND_LINE_ARG_PREFIX + "-projectLibrarySearchFolder"));
|
Loader.COMMAND_LINE_ARG_PREFIX + "-projectLibrarySearchFolder"));
|
||||||
list.add(new Option(LOCAL_LIBRARY_OPTION_NAME, LOCAL_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
list.add(new Option(LOAD_LIBRARY_OPTION_NAME, LOAD_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
||||||
Loader.COMMAND_LINE_ARG_PREFIX + "-loadLocalLibraries"));
|
Loader.COMMAND_LINE_ARG_PREFIX + "-loadLibraries"));
|
||||||
list.add(new Option(SYSTEM_LIBRARY_OPTION_NAME, SYSTEM_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
list.add(new LibrarySearchPathDummyOption(LIBRARY_SEARCH_PATH_DUMMY_OPTION_NAME));
|
||||||
Loader.COMMAND_LINE_ARG_PREFIX + "-loadSystemLibraries"));
|
|
||||||
list.add(new Option(DEPTH_OPTION_NAME, DEPTH_OPTION_DEFAULT, Integer.class,
|
list.add(new Option(DEPTH_OPTION_NAME, DEPTH_OPTION_DEFAULT, Integer.class,
|
||||||
Loader.COMMAND_LINE_ARG_PREFIX + "-libraryLoadDepth"));
|
Loader.COMMAND_LINE_ARG_PREFIX + "-libraryLoadDepth"));
|
||||||
list.add(new Option(LIBRARY_DEST_FOLDER_OPTION_NAME, LIBRARY_DEST_FOLDER_OPTION_DEFAULT,
|
list.add(new DomainFolderOption(LIBRARY_DEST_FOLDER_OPTION_NAME,
|
||||||
String.class, Loader.COMMAND_LINE_ARG_PREFIX + "-libraryDestinationFolder"));
|
Loader.COMMAND_LINE_ARG_PREFIX + "-libraryDestinationFolder"));
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +197,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
for (Option option : options) {
|
for (Option option : options) {
|
||||||
String name = option.getName();
|
String name = option.getName();
|
||||||
if (name.equals(LINK_EXISTING_OPTION_NAME) ||
|
if (name.equals(LINK_EXISTING_OPTION_NAME) ||
|
||||||
name.equals(LOCAL_LIBRARY_OPTION_NAME) ||
|
name.equals(LOAD_LIBRARY_OPTION_NAME)) {
|
||||||
name.equals(SYSTEM_LIBRARY_OPTION_NAME)) {
|
|
||||||
if (!Boolean.class.isAssignableFrom(option.getValueClass())) {
|
if (!Boolean.class.isAssignableFrom(option.getValueClass())) {
|
||||||
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
||||||
}
|
}
|
||||||
|
@ -271,27 +268,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if local libraries should be loaded. Local libraries are libraries that live
|
* Checks to see if libraries from disk should be loaded
|
||||||
* in the same directory as the imported program.
|
|
||||||
*
|
*
|
||||||
* @param options a {@link List} of {@link Option}s
|
* @param options a {@link List} of {@link Option}s
|
||||||
* @return True if local libraries should be loaded; otherwise, false
|
* @return True if libraries from disk should be loaded; otherwise, false
|
||||||
*/
|
*/
|
||||||
protected boolean isLoadLocalLibraries(List<Option> options) {
|
protected boolean isLoadLibraries(List<Option> options) {
|
||||||
return OptionUtils.getOption(LOCAL_LIBRARY_OPTION_NAME, options,
|
return OptionUtils.getOption(LOAD_LIBRARY_OPTION_NAME, options,
|
||||||
LOCAL_LIBRARY_OPTION_DEFAULT);
|
LOAD_LIBRARY_OPTION_DEFAULT);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks to see if system libraries should be loaded. System libraries are libraries that live
|
|
||||||
* in the directories specified in the GUI path list.
|
|
||||||
*
|
|
||||||
* @param options a {@link List} of {@link Option}s
|
|
||||||
* @return True if system libraries should be loaded; otherwise, false
|
|
||||||
*/
|
|
||||||
protected boolean isLoadSystemLibraries(List<Option> options) {
|
|
||||||
return OptionUtils.getOption(SYSTEM_LIBRARY_OPTION_NAME, options,
|
|
||||||
SYSTEM_LIBRARY_OPTION_DEFAULT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -348,7 +332,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (project == null || libraryDestinationFolderPath == null) {
|
if (project == null || libraryDestinationFolderPath == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!isLoadLocalLibraries(options) && !isLoadSystemLibraries(options)) {
|
if (!isLoadLibraries(options)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return project.getProjectData().getFolder(libraryDestinationFolderPath);
|
return project.getProjectData().getFolder(libraryDestinationFolderPath);
|
||||||
|
@ -472,14 +456,11 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
Set<String> processed = new TreeSet<>(getLibraryNameComparator());
|
Set<String> processed = new TreeSet<>(getLibraryNameComparator());
|
||||||
Queue<UnprocessedLibrary> unprocessed =
|
Queue<UnprocessedLibrary> unprocessed =
|
||||||
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
||||||
boolean loadLocalLibraries = isLoadLocalLibraries(options);
|
boolean loadLibraries = isLoadLibraries(options);
|
||||||
boolean loadSystemLibraries = isLoadSystemLibraries(options);
|
|
||||||
List<FileSystemSearchPath> customSearchPaths =
|
List<FileSystemSearchPath> customSearchPaths =
|
||||||
getCustomLibrarySearchPaths(provider, options, log, monitor);
|
getCustomLibrarySearchPaths(provider, options, log, monitor);
|
||||||
List<FileSystemSearchPath> localSearchPaths =
|
List<FileSystemSearchPath> searchPaths =
|
||||||
getLocalLibrarySearchPaths(provider, options, log, monitor);
|
getLibrarySearchPaths(provider, options, log, monitor);
|
||||||
List<FileSystemSearchPath> systemSearchPaths =
|
|
||||||
getSystemLibrarySearchPaths(options, log, monitor);
|
|
||||||
DomainFolder linkSearchFolder = getLinkSearchFolder(project, projectFolderPath, options);
|
DomainFolder linkSearchFolder = getLinkSearchFolder(project, projectFolderPath, options);
|
||||||
String libraryDestFolderPath =
|
String libraryDestFolderPath =
|
||||||
getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
||||||
|
@ -507,9 +488,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
log.appendMsg("Found %s in %s...".formatted(libraryName, linkSearchFolder));
|
log.appendMsg("Found %s in %s...".formatted(libraryName, linkSearchFolder));
|
||||||
log.appendMsg("------------------------------------------------\n");
|
log.appendMsg("------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
else if (!customSearchPaths.isEmpty() || !localSearchPaths.isEmpty() ||
|
else if (!customSearchPaths.isEmpty() || !searchPaths.isEmpty()) {
|
||||||
!systemSearchPaths.isEmpty()) {
|
// Note that it is possible to have search paths with those
|
||||||
// Note that it is possible to have local (or system) search paths with those
|
|
||||||
// options turned off (if shouldSearchAllPaths() is overridden to return true).
|
// options turned off (if shouldSearchAllPaths() is overridden to return true).
|
||||||
// In this case, we still want to process those libraries, but we
|
// In this case, we still want to process those libraries, but we
|
||||||
// do not want to save them, so they can be released.
|
// do not want to save them, so they can be released.
|
||||||
|
@ -528,34 +508,15 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
loadedPrograms.add(loadedLibrary);
|
loadedPrograms.add(loadedLibrary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!loaded && !localSearchPaths.isEmpty()) {
|
if (!loaded && !searchPaths.isEmpty()) {
|
||||||
log.appendMsg("Searching %d local path%s for library %s...".formatted(
|
log.appendMsg("Searching %d path%s for library %s...".formatted(
|
||||||
localSearchPaths.size(), localSearchPaths.size() > 1 ? "s" : "",
|
searchPaths.size(), searchPaths.size() > 1 ? "s" : "", libraryName));
|
||||||
libraryName));
|
|
||||||
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(libraryName,
|
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(libraryName,
|
||||||
provider, localSearchPaths, libraryDestFolderPath, unprocessed, depth,
|
provider, searchPaths, libraryDestFolderPath, unprocessed, depth,
|
||||||
desiredLoadSpec, options, log, consumer, monitor);
|
desiredLoadSpec, options, log, consumer, monitor);
|
||||||
if (loadedLibrary != null) {
|
if (loadedLibrary != null) {
|
||||||
found = true;
|
found = true;
|
||||||
if (loadLocalLibraries) {
|
if (loadLibraries) {
|
||||||
loaded = true;
|
|
||||||
loadedPrograms.add(loadedLibrary);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
loadedLibrary.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!loaded && !systemSearchPaths.isEmpty()) {
|
|
||||||
log.appendMsg("Searching %d system path%s for library %s...".formatted(
|
|
||||||
systemSearchPaths.size(), systemSearchPaths.size() > 1 ? "s" : "",
|
|
||||||
libraryName));
|
|
||||||
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(libraryName,
|
|
||||||
provider, systemSearchPaths, libraryDestFolderPath, unprocessed, depth,
|
|
||||||
desiredLoadSpec, options, log, consumer, monitor);
|
|
||||||
if (loadedLibrary != null) {
|
|
||||||
found = true;
|
|
||||||
if (loadSystemLibraries) {
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
loadedPrograms.add(loadedLibrary);
|
loadedPrograms.add(loadedLibrary);
|
||||||
}
|
}
|
||||||
|
@ -586,7 +547,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedPrograms, consumer);
|
release(loadedPrograms, consumer);
|
||||||
}
|
}
|
||||||
Stream.of(customSearchPaths, localSearchPaths, systemSearchPaths)
|
Stream.of(customSearchPaths, searchPaths)
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.forEach(fsSearchPath -> {
|
.forEach(fsSearchPath -> {
|
||||||
if (!fsSearchPath.fsRef().isClosed()) {
|
if (!fsSearchPath.fsRef().isClosed()) {
|
||||||
|
@ -1068,52 +1029,20 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to search
|
* Gets a {@link List} of priority-ordered {@link FileSystemSearchPath}s used to search for
|
||||||
* for libraries
|
* libraries
|
||||||
*
|
*
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
* @param provider The {@link ByteProvider} of the program being loaded
|
||||||
* @param options The options
|
* @param options The options
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to
|
* @return A {@link List} of priority-ordered {@link FileSystemSearchPath}s used to search for
|
||||||
* search for libraries
|
* libraries
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private List<FileSystemSearchPath> getLocalLibrarySearchPaths(ByteProvider provider,
|
private List<FileSystemSearchPath> getLibrarySearchPaths(ByteProvider provider,
|
||||||
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
if (!isLoadLocalLibraries(options) && !shouldSearchAllPaths(options)) {
|
if (!isLoadLibraries(options) && !shouldSearchAllPaths(options)) {
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
List<FileSystemSearchPath> result = new ArrayList<>();
|
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
|
||||||
FSRL providerFsrl = provider.getFSRL();
|
|
||||||
if (providerFsrl != null) {
|
|
||||||
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
|
|
||||||
GFile parentFile = fileRef.file.getParentFile();
|
|
||||||
File f = new File(parentFile.getPath()); // File API will sanitize Windows-style paths
|
|
||||||
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
log.appendException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to search
|
|
||||||
* for libraries
|
|
||||||
*
|
|
||||||
* @param options The options
|
|
||||||
* @param log The log
|
|
||||||
* @param monitor A cancelable task monitor
|
|
||||||
* @return A {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to
|
|
||||||
* search for libraries
|
|
||||||
* @throws CancelledException if the user cancelled the load
|
|
||||||
*/
|
|
||||||
private List<FileSystemSearchPath> getSystemLibrarySearchPaths(List<Option> options,
|
|
||||||
MessageLog log, TaskMonitor monitor) throws CancelledException {
|
|
||||||
if (!isLoadSystemLibraries(options) && !shouldSearchAllPaths(options)) {
|
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1121,7 +1050,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
List<FileSystemSearchPath> result = new ArrayList<>();
|
List<FileSystemSearchPath> result = new ArrayList<>();
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
for (FSRL fsrl : LibrarySearchPathManager.getLibraryFsrlList(log, monitor)) {
|
for (FSRL fsrl : LibrarySearchPathManager.getLibraryFsrlList(provider, log, monitor)) {
|
||||||
if (fsService.isLocal(fsrl)) {
|
if (fsService.isLocal(fsrl)) {
|
||||||
try {
|
try {
|
||||||
FileSystemRef fileRef =
|
FileSystemRef fileRef =
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class LibraryPathsDialog extends AbstractPathsDialog {
|
||||||
protected PathnameTablePanel newPathnameTablePanel() {
|
protected PathnameTablePanel newPathnameTablePanel() {
|
||||||
// disable edits, add to top, ordered
|
// disable edits, add to top, ordered
|
||||||
PathnameTablePanel panel =
|
PathnameTablePanel panel =
|
||||||
new PathnameTablePanel(loadPaths(), this::reset, false, true, true);
|
new PathnameTablePanel(loadPaths(), this::reset, false, true, true, true);
|
||||||
panel.setFileChooserProperties("Select Directory or Filesystem",
|
panel.setFileChooserProperties("Select Directory or Filesystem",
|
||||||
"LibrarySearchDirectory", GhidraFileChooserMode.FILES_AND_DIRECTORIES, false, null);
|
"LibrarySearchDirectory", GhidraFileChooserMode.FILES_AND_DIRECTORIES, false, null);
|
||||||
return panel;
|
return panel;
|
||||||
|
|
|
@ -34,7 +34,6 @@ import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.services.FileImporterService;
|
import ghidra.app.services.FileImporterService;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.LibrarySearchPathManager;
|
|
||||||
import ghidra.app.util.opinion.LoaderMap;
|
import ghidra.app.util.opinion.LoaderMap;
|
||||||
import ghidra.app.util.opinion.LoaderService;
|
import ghidra.app.util.opinion.LoaderService;
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
@ -44,7 +43,6 @@ import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
import ghidra.framework.main.*;
|
import ghidra.framework.main.*;
|
||||||
import ghidra.framework.main.datatree.*;
|
import ghidra.framework.main.datatree.*;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.options.SaveState;
|
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
@ -119,23 +117,6 @@ public class ImporterPlugin extends Plugin
|
||||||
setupBatchImportAction();
|
setupBatchImportAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readConfigState(SaveState saveState) {
|
|
||||||
super.readConfigState(saveState);
|
|
||||||
String[] paths = saveState.getStrings("library search paths", null);
|
|
||||||
if (paths != null) {
|
|
||||||
LibrarySearchPathManager.setLibraryPaths(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
|
||||||
super.writeConfigState(saveState);
|
|
||||||
|
|
||||||
String[] paths = LibrarySearchPathManager.getLibraryPaths();
|
|
||||||
saveState.putStrings("library search paths", paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispose() {
|
protected void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
|
@ -97,12 +97,7 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isLoadLocalLibraries(List<Option> options) {
|
protected boolean isLoadLibraries(List<Option> options) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isLoadSystemLibraries(List<Option> options) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,12 +97,7 @@ public class MachoFileSetExtractLoader extends MachoLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isLoadLocalLibraries(List<Option> options) {
|
protected boolean isLoadLibraries(List<Option> options) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isLoadSystemLibraries(List<Option> options) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class PathnameTablePanel extends JPanel {
|
||||||
private GhidraFileFilter filter;
|
private GhidraFileFilter filter;
|
||||||
private boolean addToTop;
|
private boolean addToTop;
|
||||||
private boolean ordered;
|
private boolean ordered;
|
||||||
|
private boolean supportsDotPath;
|
||||||
|
|
||||||
private Callback resetCallback;
|
private Callback resetCallback;
|
||||||
|
|
||||||
|
@ -95,12 +96,15 @@ public class PathnameTablePanel extends JPanel {
|
||||||
* false.
|
* false.
|
||||||
* @param ordered true if the order of entries matters. If so, up and down buttons are provided
|
* @param ordered true if the order of entries matters. If so, up and down buttons are provided
|
||||||
* so the user may arrange the entries. If not, entries are sorted alphabetically.
|
* so the user may arrange the entries. If not, entries are sorted alphabetically.
|
||||||
|
* @param supportsDotPath true if the add button should support adding the "." path. If so,
|
||||||
|
* the user will be prompted to choose from a file browser, or adding ".".
|
||||||
*/
|
*/
|
||||||
public PathnameTablePanel(String[] paths, Callback resetCallback, boolean enableEdits,
|
public PathnameTablePanel(String[] paths, Callback resetCallback, boolean enableEdits,
|
||||||
boolean addToTop, boolean ordered) {
|
boolean addToTop, boolean ordered, boolean supportsDotPath) {
|
||||||
super(new BorderLayout(5, 5));
|
super(new BorderLayout(5, 5));
|
||||||
this.addToTop = addToTop;
|
this.addToTop = addToTop;
|
||||||
this.ordered = ordered;
|
this.ordered = ordered;
|
||||||
|
this.supportsDotPath = supportsDotPath;
|
||||||
this.resetCallback = resetCallback;
|
this.resetCallback = resetCallback;
|
||||||
tableModel = new PathnameTableModel(paths, enableEdits);
|
tableModel = new PathnameTableModel(paths, enableEdits);
|
||||||
create();
|
create();
|
||||||
|
@ -316,6 +320,17 @@ public class PathnameTablePanel extends JPanel {
|
||||||
|
|
||||||
private void add() {
|
private void add() {
|
||||||
|
|
||||||
|
if (supportsDotPath && !Arrays.stream(getPaths()).anyMatch(p -> p.equals("."))) {
|
||||||
|
int selection =
|
||||||
|
OptionDialog.showOptionNoCancelDialog(this, "Add Path", "Choose how to add a path:",
|
||||||
|
"File Chooser", "Program's Import Location", OptionDialog.QUESTION_MESSAGE);
|
||||||
|
|
||||||
|
if (selection == OptionDialog.OPTION_TWO) {
|
||||||
|
tableModel.addPaths(new String[] { "." }, addToTop, !ordered);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GhidraFileChooser fileChooser = new GhidraFileChooser(this);
|
GhidraFileChooser fileChooser = new GhidraFileChooser(this);
|
||||||
fileChooser.setMultiSelectionEnabled(allowMultiFileSelection);
|
fileChooser.setMultiSelectionEnabled(allowMultiFileSelection);
|
||||||
fileChooser.setFileSelectionMode(fileChooserMode);
|
fileChooser.setFileSelectionMode(fileChooserMode);
|
||||||
|
@ -363,7 +378,7 @@ public class PathnameTablePanel extends JPanel {
|
||||||
String confirmation = """
|
String confirmation = """
|
||||||
<html><body width="200px">
|
<html><body width="200px">
|
||||||
Are you sure you would like to reset the paths to the default list?
|
Are you sure you would like to reset the paths to the default list?
|
||||||
This will remove all paths manually added.
|
This will remove all paths manually added and cannot be later cancelled.
|
||||||
</html>""";
|
</html>""";
|
||||||
String header = "Reset Paths?";
|
String header = "Reset Paths?";
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
[<a href="#commit">-commit ["<comment>"]</a>]
|
[<a href="#commit">-commit ["<comment>"]</a>]
|
||||||
[<a href="#okToDelete">-okToDelete</a>]
|
[<a href="#okToDelete">-okToDelete</a>]
|
||||||
[<a href="#max-cpu">-max-cpu <max cpu cores to use></a>]
|
[<a href="#max-cpu">-max-cpu <max cpu cores to use></a>]
|
||||||
|
[<a href="#librarySearchPaths">-librarySearchPaths <path1>[;<path2>...]</a>]
|
||||||
[<a href="#loader">-loader <desired loader name></a>]
|
[<a href="#loader">-loader <desired loader name></a>]
|
||||||
|
|
||||||
</PRE>
|
</PRE>
|
||||||
|
@ -584,6 +585,14 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
<a name="librarySearchPaths"><typewriter>-librarySearchPaths <path1>[;<path2>...]</typewriter></a><br>
|
||||||
|
Specifies an ordered list of library search paths to use during import instead of the default.
|
||||||
|
Search paths may be either full system paths or "FSRLs".
|
||||||
|
</LI>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
<LI>
|
<LI>
|
||||||
<a name="loader"><typewriter>-loader <desired loader name></typewriter></a><br>
|
<a name="loader"><typewriter>-loader <desired loader name></typewriter></a><br>
|
||||||
Forces the file to be imported using a specific loader. <br><br>
|
Forces the file to be imported using a specific loader. <br><br>
|
||||||
|
@ -606,8 +615,7 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
<LI><typewriter>-loader-anchorLabels <true|false></typewriter></LI>
|
<LI><typewriter>-loader-anchorLabels <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-linkExistingProjectLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-linkExistingProjectLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-projectLibrarySearchFolder <project path></typewriter></LI>
|
<LI><typewriter>-loader-projectLibrarySearchFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-loadLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadSystemLibraries <true|false></typewriter></LI>
|
|
||||||
<LI><typewriter>-loader-libraryLoadDepth <depth></typewriter></LI>
|
<LI><typewriter>-loader-libraryLoadDepth <depth></typewriter></LI>
|
||||||
<LI><typewriter>-loader-libraryDestinationFolder <project path></typewriter></LI>
|
<LI><typewriter>-loader-libraryDestinationFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-applyRelocations <true|false></typewriter></LI>
|
<LI><typewriter>-loader-applyRelocations <true|false></typewriter></LI>
|
||||||
|
@ -623,8 +631,7 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
<LI><typewriter>-loader-anchorLabels <true|false></typewriter></LI>
|
<LI><typewriter>-loader-anchorLabels <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-linkExistingProjectLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-linkExistingProjectLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-projectLibrarySearchFolder <project path></typewriter></LI>
|
<LI><typewriter>-loader-projectLibrarySearchFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-loadLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadSystemLibraries <true|false></typewriter></LI>
|
|
||||||
<LI><typewriter>-loader-libraryLoadDepth <depth></typewriter></LI>
|
<LI><typewriter>-loader-libraryLoadDepth <depth></typewriter></LI>
|
||||||
<LI><typewriter>-loader-libraryDestinationFolder <project path></typewriter></LI>
|
<LI><typewriter>-loader-libraryDestinationFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-ordinalLookup <true|false></typewriter></LI>
|
<LI><typewriter>-loader-ordinalLookup <true|false></typewriter></LI>
|
||||||
|
@ -637,8 +644,7 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
<LI><typewriter>-loader-anchorLabels <true|false></typewriter></LI>
|
<LI><typewriter>-loader-anchorLabels <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-linkExistingProjectLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-linkExistingProjectLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-projectLibrarySearchFolder <project path></typewriter></LI>
|
<LI><typewriter>-loader-projectLibrarySearchFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-loadLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadSystemLibraries <true|false></typewriter></LI>
|
|
||||||
<LI><typewriter>-loader-libraryLoadDepth <depth></typewriter></LI>
|
<LI><typewriter>-loader-libraryLoadDepth <depth></typewriter></LI>
|
||||||
<LI><typewriter>-loader-libraryDestinationFolder <project path></typewriter></LI>
|
<LI><typewriter>-loader-libraryDestinationFolder <project path></typewriter></LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class PathnameTablePanelTest extends AbstractDockingTest {
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
// enable edits, add to bottom, ordered
|
// enable edits, add to bottom, ordered
|
||||||
panel = new PathnameTablePanel(tablePaths, () -> reset(), true, false, true);
|
panel = new PathnameTablePanel(tablePaths, () -> reset(), true, false, true, false);
|
||||||
table = panel.getTable();
|
table = panel.getTable();
|
||||||
frame = new JFrame("Test");
|
frame = new JFrame("Test");
|
||||||
frame.getContentPane().add(panel);
|
frame.getContentPane().add(panel);
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
[-propertiesPath "<path1>[;<path2>...]"]
|
[-propertiesPath "<path1>[;<path2>...]"]
|
||||||
[-log <path to log file>] [-scriptlog <path to script log file>]
|
[-log <path to log file>] [-scriptlog <path to script log file>]
|
||||||
[-overwrite] [-recursive [<depth>]] [-readOnly] [-deleteProject]
|
[-overwrite] [-recursive [<depth>]] [-readOnly] [-deleteProject]
|
||||||
[-noanalysis]
|
[-noanalysis] [-librarySearchPaths <path1>[;<path2>...]]
|
||||||
[-processor <languageID>] [-cspec <compilerSpecID>]
|
[-processor <languageID>] [-cspec <compilerSpecID>]
|
||||||
[-analysisTimeoutPerFile <timeout in seconds>]
|
[-analysisTimeoutPerFile <timeout in seconds>]
|
||||||
[-keystore <KeystorePath>] [-connect [<userID>]]
|
[-keystore <KeystorePath>] [-connect [<userID>]]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue