mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-28 13:30:21 +00:00
Merge branch 'GP-2604_ryanmkurtz_more_library_options'
This commit is contained in:
commit
bb68f37885
|
@ -29,38 +29,27 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<UL>
|
<UL>
|
||||||
|
<LI>Android APK</LI>
|
||||||
<LI>Common Object File Format (COFF)</LI>
|
<LI>Common Object File Format (COFF)</LI>
|
||||||
|
<LI>Dalvik Executable (DEX)</LI>
|
||||||
<LI>Debug Symbols (DBG)</LI>
|
<LI>Debug Symbols (DBG)</LI>
|
||||||
|
<LI>Dump File Loader</LI>
|
||||||
<LI>DYLD Shared Cache</LI>
|
<LI>DYLD Shared Cache</LI>
|
||||||
|
|
||||||
<LI>Executable and Linking Format (ELF)</LI>
|
<LI>Executable and Linking Format (ELF)</LI>
|
||||||
|
|
||||||
<LI>Ghidra Data Type Archive Format</LI>
|
<LI>Ghidra Data Type Archive Format</LI>
|
||||||
|
|
||||||
<LI>GZF Input Format</LI>
|
<LI>GZF Input Format</LI>
|
||||||
|
|
||||||
<LI>Intel Hex</LI>
|
<LI>Intel Hex</LI>
|
||||||
|
<LI>Java Class File</LI>
|
||||||
<LI>Mac OS X Mach-O</LI>
|
<LI>Mac OS X Mach-O</LI>
|
||||||
|
|
||||||
<LI>Module Definition (DEF)</LI>
|
<LI>Module Definition (DEF)</LI>
|
||||||
|
|
||||||
<LI>Motorola Hex</LI>
|
<LI>Motorola Hex</LI>
|
||||||
|
|
||||||
<LI>New Executable (NE)</LI>
|
<LI>New Executable (NE)</LI>
|
||||||
|
|
||||||
<LI>Old-style DOS Executable (MZ)</LI>
|
<LI>Old-style DOS Executable (MZ)</LI>
|
||||||
|
|
||||||
<LI>Portable Executable (PE)</LI>
|
<LI>Portable Executable (PE)</LI>
|
||||||
|
|
||||||
<LI>Preferred Executable Format (PEF)</LI>
|
<LI>Preferred Executable Format (PEF)</LI>
|
||||||
|
|
||||||
<LI>Program Mapfile (MAP)</LI>
|
<LI>Program Mapfile (MAP)</LI>
|
||||||
|
|
||||||
<LI>Raw Binary</LI>
|
<LI>Raw Binary</LI>
|
||||||
|
<LI>Relocatable Object Module Format (OMF)</LI>
|
||||||
<LI>XML Input Format</LI>
|
<LI>XML Input Format</LI>
|
||||||
</UL>
|
</UL>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
@ -281,8 +270,22 @@
|
||||||
those symbols will remain at the address they were originally placed. If the option is
|
those symbols will remain at the address they were originally placed. If the option is
|
||||||
off, the symbols will move with the image base or the memory block.</P>
|
off, the symbols will move with the image base or the memory block.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4>Link Existing Project Libraries</H4>
|
||||||
|
|
||||||
<H4>Load Local Libraries</H4>
|
<BLOCKQUOTE>
|
||||||
|
<P> Searches the project for existing library programs and creates external references to
|
||||||
|
them.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4>Project Library Search Folder</H4>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>The project folder that will get searched for existing library programs. If left
|
||||||
|
empty, the folder that the main program is being imported to will be searched.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4>Load Local Libraries From Disk</H4>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>Searches the executable's directory to recursively resolve the external libraries used
|
<P>Searches the executable's directory to recursively resolve the external libraries used
|
||||||
|
@ -292,7 +295,7 @@
|
||||||
in these programs will be resolved.<BR>
|
in these programs will be resolved.<BR>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H4>Load System Libraries</H4>
|
<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
|
||||||
|
@ -309,6 +312,13 @@
|
||||||
<P>Specifies how many levels deep the depth-first library dependency tree will be
|
<P>Specifies how many levels deep the depth-first library dependency tree will be
|
||||||
traversed when loading local or system libraries.</P>
|
traversed when loading local or system libraries.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4>Library Destination Folder</H4>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>The project folder where newly loaded library programs will get created. If left
|
||||||
|
empty, they will get created in the same folder as the main program being imported.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3>COFF Options<A name="Options_Common Object_File_Format__COFF_"/></H3>
|
<H3>COFF Options<A name="Options_Common Object_File_Format__COFF_"/></H3>
|
||||||
|
|
|
@ -28,12 +28,15 @@ import javax.swing.event.DocumentListener;
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
|
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
|
import docking.options.editor.ButtonPanelFactory;
|
||||||
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.AbstractLibrarySupportLoader;
|
import ghidra.app.util.opinion.AbstractLibrarySupportLoader;
|
||||||
import ghidra.app.util.opinion.LibraryPathsDialog;
|
import ghidra.app.util.opinion.LibraryPathsDialog;
|
||||||
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
import ghidra.framework.model.DomainFolder;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.layout.*;
|
import ghidra.util.layout.*;
|
||||||
|
@ -43,7 +46,7 @@ import ghidra.util.layout.*;
|
||||||
* in a list of Options and generates editors for each of them on th fly.
|
* in a list of Options and generates editors for each of them on th fly.
|
||||||
*/
|
*/
|
||||||
public class OptionsEditorPanel extends JPanel {
|
public class OptionsEditorPanel extends JPanel {
|
||||||
private static final int MAX_PER_COLUMN = 10;
|
private static final int MAX_PER_COLUMN = 11;
|
||||||
private static final int MAX_BOOLEANS_WITH_SELECT_ALL = 5;
|
private static final int MAX_BOOLEANS_WITH_SELECT_ALL = 5;
|
||||||
private int columns;
|
private int columns;
|
||||||
private AddressFactoryService addressFactoryService;
|
private AddressFactoryService addressFactoryService;
|
||||||
|
@ -177,9 +180,13 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
|
|
||||||
public Component getEditorComponent(Option option) {
|
public Component getEditorComponent(Option option) {
|
||||||
|
|
||||||
//special case for load library paths
|
// 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)) {
|
if (option.getName().equals(AbstractLibrarySupportLoader.SYSTEM_LIBRARY_OPTION_NAME)) {
|
||||||
return buildLoadLibraryPathsEditor(option);
|
return buildPathsEditor(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
Component customEditorComponent = option.getCustomEditorComponent();
|
Component customEditorComponent = option.getCustomEditorComponent();
|
||||||
|
@ -216,7 +223,7 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component buildLoadLibraryPathsEditor(Option option) {
|
private Component buildPathsEditor(Option option) {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
JButton button = new JButton("Edit Paths");
|
JButton button = new JButton("Edit Paths");
|
||||||
button.addActionListener(
|
button.addActionListener(
|
||||||
|
@ -233,6 +240,27 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Component buildProjectFolderEditor(Option option) {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
JTextField textField = new JTextField();
|
||||||
|
textField.setEditable(false);
|
||||||
|
JButton button = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE);
|
||||||
|
button.addActionListener(e -> {
|
||||||
|
DataTreeDialog dataTreeDialog = new DataTreeDialog(this,
|
||||||
|
"Choose a project folder", DataTreeDialog.CHOOSE_FOLDER);
|
||||||
|
dataTreeDialog.setSelectedFolder(null);
|
||||||
|
dataTreeDialog.showComponent();
|
||||||
|
DomainFolder folder = dataTreeDialog.getDomainFolder();
|
||||||
|
if (folder != null) {
|
||||||
|
textField.setText(folder.getPathname());
|
||||||
|
option.setValue(folder.getPathname());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
panel.add(textField, BorderLayout.CENTER);
|
||||||
|
panel.add(button, BorderLayout.EAST);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
private Component getAddressSpaceEditorComponent(Option option) {
|
private Component getAddressSpaceEditorComponent(Option option) {
|
||||||
JComboBox<AddressSpace> combo = new GComboBox<>();
|
JComboBox<AddressSpace> combo = new GComboBox<>();
|
||||||
AddressFactory addressFactory = addressFactoryService.getAddressFactory();
|
AddressFactory addressFactory = addressFactoryService.getAddressFactory();
|
||||||
|
|
|
@ -50,15 +50,24 @@ import utilities.util.FileUtilities;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader {
|
public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader {
|
||||||
|
|
||||||
public static final String LOCAL_LIBRARY_OPTION_NAME = "Load Local Libraries";
|
public static final String LINK_EXISTING_OPTION_NAME = "Link Existing Project Libraries";
|
||||||
|
static final boolean LINK_EXISTING_OPTION_DEFAULT = true;
|
||||||
|
|
||||||
|
public static final String LINK_SEARCH_FOLDER_OPTION_NAME = "Project Library Search Folder";
|
||||||
|
static final String LINK_SEARCH_FOLDER_OPTION_DEFAULT = "";
|
||||||
|
|
||||||
|
public static final String LOCAL_LIBRARY_OPTION_NAME = "Load Local Libraries From Disk";
|
||||||
static final boolean LOCAL_LIBRARY_OPTION_DEFAULT = false;
|
static final boolean LOCAL_LIBRARY_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
public static final String SYSTEM_LIBRARY_OPTION_NAME = "Load System Libraries";
|
public static final String SYSTEM_LIBRARY_OPTION_NAME = "Load System Libraries From Disk";
|
||||||
static final boolean SYSTEM_LIBRARY_OPTION_DEFAULT = false;
|
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;
|
||||||
|
|
||||||
|
public static final String LIBRARY_DEST_FOLDER_OPTION_NAME = "Library Destination Folder";
|
||||||
|
static final String LIBRARY_DEST_FOLDER_OPTION_DEFAULT = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads bytes in a particular format into the given {@link Program}.
|
* Loads bytes in a particular format into the given {@link Program}.
|
||||||
*
|
*
|
||||||
|
@ -76,11 +85,11 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
throws CancelledException, IOException;
|
throws CancelledException, IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
|
|
||||||
List<Program> programList = new ArrayList<>();
|
List<LoadedProgram> loadedProgramList = new ArrayList<>();
|
||||||
List<String> libraryNameList = new ArrayList<>();
|
List<String> libraryNameList = new ArrayList<>();
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -88,21 +97,19 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
// Load the primary program
|
// Load the primary program
|
||||||
Program program = doLoad(provider, programName, programFolder, loadSpec,
|
Program program = doLoad(provider, programName, programFolder, loadSpec,
|
||||||
libraryNameList, options, consumer, log, monitor);
|
libraryNameList, options, consumer, log, monitor);
|
||||||
programList.add(program);
|
loadedProgramList.add(new LoadedProgram(program, programFolder));
|
||||||
|
|
||||||
// Load the libraries, if applicable
|
// Load the libraries
|
||||||
if (shouldLoadLibraries(options)) {
|
List<LoadedProgram> libraries = loadLibraries(provider, program, programFolder,
|
||||||
List<Program> libraries = loadLibraries(provider, program, programFolder, loadSpec,
|
loadSpec, options, log, consumer, libraryNameList, monitor);
|
||||||
options, log, consumer, libraryNameList, monitor);
|
loadedProgramList.addAll(libraries);
|
||||||
programList.addAll(libraries);
|
|
||||||
}
|
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
return programList;
|
return loadedProgramList;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(programList, consumer);
|
release(loadedProgramList, consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,11 +133,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Program> loadedPrograms, DomainFolder folder,
|
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
if (isLinkExistingLibraries(options) || isLoadLocalLibraries(options) ||
|
||||||
if (isLoadLocalLibraries(options) || isLoadSystemLibraries(options)) {
|
isLoadSystemLibraries(options)) {
|
||||||
fixupExternalLibraries(loadedPrograms, folder, true, messageLog, monitor);
|
DomainFolder programFolder = loadedPrograms.get(0).destinationFolder();
|
||||||
|
DomainFolder linkSearchFolder = getLinkSearchFolder(programFolder, options);
|
||||||
|
fixupExternalLibraries(loadedPrograms.stream().map(e -> e.program()).toList(),
|
||||||
|
linkSearchFolder, true, messageLog, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,12 +159,18 @@ 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,
|
||||||
|
Loader.COMMAND_LINE_ARG_PREFIX + "-linkExistingProjectLibraries"));
|
||||||
|
list.add(new Option(LINK_SEARCH_FOLDER_OPTION_NAME, LINK_SEARCH_FOLDER_OPTION_DEFAULT,
|
||||||
|
String.class, Loader.COMMAND_LINE_ARG_PREFIX + "-projectLibrarySearchFolder"));
|
||||||
list.add(new Option(LOCAL_LIBRARY_OPTION_NAME, LOCAL_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
list.add(new Option(LOCAL_LIBRARY_OPTION_NAME, LOCAL_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
||||||
Loader.COMMAND_LINE_ARG_PREFIX + "-loadLocalLibraries"));
|
Loader.COMMAND_LINE_ARG_PREFIX + "-loadLocalLibraries"));
|
||||||
list.add(new Option(SYSTEM_LIBRARY_OPTION_NAME, SYSTEM_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
list.add(new Option(SYSTEM_LIBRARY_OPTION_NAME, SYSTEM_LIBRARY_OPTION_DEFAULT, Boolean.class,
|
||||||
Loader.COMMAND_LINE_ARG_PREFIX + "-loadSystemLibraries"));
|
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,
|
||||||
|
String.class, Loader.COMMAND_LINE_ARG_PREFIX + "-libraryDestinationFolder"));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +180,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
for (Option option : options) {
|
for (Option option : options) {
|
||||||
String name = option.getName();
|
String name = option.getName();
|
||||||
if (name.equals(LOCAL_LIBRARY_OPTION_NAME) || name.equals(SYSTEM_LIBRARY_OPTION_NAME)) {
|
if (name.equals(LINK_EXISTING_OPTION_NAME) ||
|
||||||
|
name.equals(LOCAL_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();
|
||||||
}
|
}
|
||||||
|
@ -174,11 +192,66 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (name.equals(LINK_SEARCH_FOLDER_OPTION_NAME) ||
|
||||||
|
name.equals(LIBRARY_DEST_FOLDER_OPTION_NAME)) {
|
||||||
|
if (!String.class.isAssignableFrom(option.getValueClass())) {
|
||||||
|
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.validateOptions(provider, loadSpec, options, program);
|
return super.validateOptions(provider, loadSpec, options, program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if existing libraries should be linked
|
||||||
|
*
|
||||||
|
* @param options a {@link List} of {@link Option}s
|
||||||
|
* @return True if existing libraries should be linked; otherwise, false
|
||||||
|
*/
|
||||||
|
protected boolean isLinkExistingLibraries(List<Option> options) {
|
||||||
|
boolean isLinkExistingLibraries = LINK_EXISTING_OPTION_DEFAULT;
|
||||||
|
if (options != null) {
|
||||||
|
for (Option option : options) {
|
||||||
|
String optName = option.getName();
|
||||||
|
if (optName.equals(LINK_EXISTING_OPTION_NAME)) {
|
||||||
|
isLinkExistingLibraries = (Boolean) option.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isLinkExistingLibraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link DomainFolder project folder} to search for existing libraries
|
||||||
|
*
|
||||||
|
* @param programFolder The {@link DomainFolder} that the main program is being loaded into
|
||||||
|
* @param options a {@link List} of {@link Option}s
|
||||||
|
* @return The path of the project folder to search for existing libraries, or null if no
|
||||||
|
* project folders should be searched
|
||||||
|
*/
|
||||||
|
protected DomainFolder getLinkSearchFolder(DomainFolder programFolder,
|
||||||
|
List<Option> options) {
|
||||||
|
if (!shouldSearchAllPaths(options) && !isLinkExistingLibraries(options)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String folderPath = LINK_SEARCH_FOLDER_OPTION_DEFAULT;
|
||||||
|
if (options != null) {
|
||||||
|
for (Option option : options) {
|
||||||
|
String optName = option.getName();
|
||||||
|
if (optName.equals(LINK_SEARCH_FOLDER_OPTION_NAME)) {
|
||||||
|
folderPath = (String) option.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folderPath.equals(LINK_SEARCH_FOLDER_OPTION_DEFAULT)) {
|
||||||
|
return programFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return programFolder.getProjectData().getFolder(FilenameUtils.separatorsToUnix(folderPath));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if local libraries should be loaded. Local libraries are libraries that live
|
* Checks to see if local libraries should be loaded. Local libraries are libraries that live
|
||||||
* in the same directory as the imported program.
|
* in the same directory as the imported program.
|
||||||
|
@ -239,23 +312,39 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether or not libraries should be loaded (local or system)
|
* Gets the {@link DomainFolder project folder} to load the libraries into
|
||||||
*
|
*
|
||||||
|
* @param programFolder The {@link DomainFolder} that the main program is being loaded into
|
||||||
* @param options a {@link List} of {@link Option}s
|
* @param options a {@link List} of {@link Option}s
|
||||||
* @return True if any libraries should be loaded (local or system); otherwise, false
|
* @return The path of the project folder to load the libraries into
|
||||||
*/
|
*/
|
||||||
protected boolean shouldLoadLibraries(List<Option> options) {
|
protected DomainFolder getLibraryDestinationFolder(DomainFolder programFolder,
|
||||||
return (isLoadLocalLibraries(options) || isLoadSystemLibraries(options)) &&
|
List<Option> options) {
|
||||||
getLibraryLoadDepth(options) > 0;
|
String folderPath = LIBRARY_DEST_FOLDER_OPTION_DEFAULT;
|
||||||
|
if (options != null) {
|
||||||
|
for (Option option : options) {
|
||||||
|
String optName = option.getName();
|
||||||
|
if (optName.equals(LIBRARY_DEST_FOLDER_OPTION_NAME)) {
|
||||||
|
folderPath = (String) option.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folderPath.equals(LIBRARY_DEST_FOLDER_OPTION_DEFAULT)) {
|
||||||
|
return programFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return programFolder.getProjectData().getFolder(FilenameUtils.separatorsToUnix(folderPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether or not to search for libraries using all possible search paths, regardless
|
* Checks whether or not to search for libraries using all possible search paths, regardless
|
||||||
* of what options are set
|
* of what options are set
|
||||||
*
|
*
|
||||||
|
* @param options a {@link List} of {@link Option}s
|
||||||
* @return True if all possible search paths should be used, regardless of what options are set
|
* @return True if all possible search paths should be used, regardless of what options are set
|
||||||
*/
|
*/
|
||||||
protected boolean shouldSearchAllPaths() {
|
protected boolean shouldSearchAllPaths(List<Option> options) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +429,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
protected boolean processLibrary(Program library, String libraryName, File libraryFile,
|
protected boolean processLibrary(Program library, String libraryName, File libraryFile,
|
||||||
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException {
|
TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
return true;
|
return isLoadLocalLibraries(options) || isLoadSystemLibraries(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -361,16 +450,18 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private List<Program> loadLibraries(ByteProvider provider, Program program,
|
private List<LoadedProgram> loadLibraries(ByteProvider provider, Program program,
|
||||||
DomainFolder programFolder, LoadSpec desiredLoadSpec, List<Option> options,
|
DomainFolder programFolder, LoadSpec desiredLoadSpec, List<Option> options,
|
||||||
MessageLog log, Object consumer, List<String> libraryNameList, TaskMonitor monitor)
|
MessageLog log, Object consumer, List<String> libraryNameList, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
List<Program> programList = new ArrayList<>();
|
List<LoadedProgram> loadedPrograms = new ArrayList<>();
|
||||||
Set<String> processed = new HashSet<>();
|
Set<String> processed = new HashSet<>();
|
||||||
Queue<UnprocessedLibrary> unprocessed =
|
Queue<UnprocessedLibrary> unprocessed =
|
||||||
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
||||||
List<String> searchPaths = getLibrarySearchPaths(provider, options);
|
List<String> searchPaths = getLibrarySearchPaths(provider, options);
|
||||||
|
DomainFolder linkSearchFolder = getLinkSearchFolder(programFolder, options);
|
||||||
|
DomainFolder libraryDestFolder = getLibraryDestinationFolder(programFolder, options);
|
||||||
|
|
||||||
while (!unprocessed.isEmpty()) {
|
while (!unprocessed.isEmpty()) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
@ -380,8 +471,12 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (depth == 0 || processed.contains(libraryName)) {
|
if (depth == 0 || processed.contains(libraryName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
processed.add(libraryName);
|
||||||
boolean foundLibrary = false;
|
boolean foundLibrary = false;
|
||||||
if (findLibrary(libraryName, programFolder) == null) {
|
if (linkSearchFolder != null && findLibrary(libraryName, linkSearchFolder) != null) {
|
||||||
|
log.appendMsg("Library " + libraryName + ": Already loaded ");
|
||||||
|
}
|
||||||
|
else if (!searchPaths.isEmpty()) {
|
||||||
String simpleLibraryName = FilenameUtils.getName(libraryName);
|
String simpleLibraryName = FilenameUtils.getName(libraryName);
|
||||||
|
|
||||||
List<File> candidateLibraryFiles =
|
List<File> candidateLibraryFiles =
|
||||||
|
@ -399,7 +494,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
foundLibrary = true;
|
foundLibrary = true;
|
||||||
if (processLibrary(library, libraryName, candidateLibraryFile, provider,
|
if (processLibrary(library, libraryName, candidateLibraryFile, provider,
|
||||||
desiredLoadSpec, options, log, monitor)) {
|
desiredLoadSpec, options, log, monitor)) {
|
||||||
programList.add(library);
|
loadedPrograms.add(new LoadedProgram(library, libraryDestFolder));
|
||||||
log.appendMsg(
|
log.appendMsg(
|
||||||
"Library " + libraryName + ": Saving " + candidateLibraryFile);
|
"Library " + libraryName + ": Saving " + candidateLibraryFile);
|
||||||
}
|
}
|
||||||
|
@ -415,14 +510,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
log.appendMsg("Library " + libraryName + ": Not found");
|
log.appendMsg("Library " + libraryName + ": Not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
log.appendMsg("Library " + libraryName + ": Already loaded ");
|
|
||||||
}
|
|
||||||
processed.add(libraryName);
|
|
||||||
}
|
}
|
||||||
log.appendMsg(
|
return loadedPrograms;
|
||||||
"Finished importing referenced libraries for: " + program.getName());
|
|
||||||
return programList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -548,7 +637,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* a {@link ByteProvider} available.
|
* a {@link ByteProvider} available.
|
||||||
*
|
*
|
||||||
* @param libraryName The name of the library to load
|
* @param libraryName The name of the library to load
|
||||||
* @param programFolder The domain folder where the new program will be stored, if null
|
* @param libraryFolder The domain folder where the new library program will be stored, if null
|
||||||
* the program should not be pre-saved. NOTE: the newly imported libraries will not be written
|
* the program should not be pre-saved. NOTE: the newly imported libraries will not be written
|
||||||
* to this folder yet, that is handled in a later follow on step.
|
* to this folder yet, that is handled in a later follow on step.
|
||||||
* @param libraryFile The library file to load
|
* @param libraryFile The library file to load
|
||||||
|
@ -563,7 +652,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws CancelledException if the user cancelled the load operation
|
* @throws CancelledException if the user cancelled the load operation
|
||||||
* @throws IOException if there was an IO-related error during the load
|
* @throws IOException if there was an IO-related error during the load
|
||||||
*/
|
*/
|
||||||
private Program loadLibrary(String libraryName, DomainFolder programFolder, File libraryFile,
|
private Program loadLibrary(String libraryName, DomainFolder libraryFolder, File libraryFile,
|
||||||
LoadSpec desiredLoadSpec, List<String> libraryNameList, List<Option> options,
|
LoadSpec desiredLoadSpec, List<String> libraryNameList, List<Option> options,
|
||||||
Object consumer, MessageLog log, TaskMonitor monitor)
|
Object consumer, MessageLog log, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
@ -579,7 +668,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Program library = doLoad(provider, libraryName, programFolder, libLoadSpec,
|
Program library = doLoad(provider, libraryName, libraryFolder, libLoadSpec,
|
||||||
libraryNameList, options, consumer, log, monitor);
|
libraryNameList, options, consumer, log, monitor);
|
||||||
|
|
||||||
if (library == null) {
|
if (library == null) {
|
||||||
|
@ -668,16 +757,16 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*
|
*
|
||||||
* @param programs the list of programs to resolve against each other. Programs not saved
|
* @param programs the list of programs to resolve against each other. Programs not saved
|
||||||
* to the project will be considered as a valid external library.
|
* to the project will be considered as a valid external library.
|
||||||
* @param domainFolder the {@link DomainFolder} folder within which imported libraries will
|
* @param searchFolder the {@link DomainFolder} which imported libraries will be searched.
|
||||||
* be searched. This folder will be searched if a library is not found within the
|
* This folder will be searched if a library is not found within the list of
|
||||||
* list of programs supplied. If null, only the list of programs will be considered.
|
* programs supplied. If null, only the list of programs will be considered.
|
||||||
* @param saveIfModified flag to have this method save any programs it modifies
|
* @param saveIfModified flag to have this method save any programs it modifies
|
||||||
* @param messageLog log for messages.
|
* @param messageLog log for messages.
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
* @throws IOException if there was an IO-related problem resolving.
|
* @throws IOException if there was an IO-related problem resolving.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
private void fixupExternalLibraries(List<Program> programs, DomainFolder domainFolder,
|
private void fixupExternalLibraries(List<Program> programs, DomainFolder searchFolder,
|
||||||
boolean saveIfModified, MessageLog messageLog, TaskMonitor monitor)
|
boolean saveIfModified, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
|
@ -703,7 +792,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
monitor.setMessage("Resolving..." + program.getName());
|
monitor.setMessage("Resolving..." + program.getName());
|
||||||
int id = program.startTransaction("Resolving external references");
|
int id = program.startTransaction("Resolving external references");
|
||||||
try {
|
try {
|
||||||
resolveExternalLibraries(program, progsByName, domainFolder, monitor, messageLog);
|
resolveExternalLibraries(program, progsByName, searchFolder, monitor, messageLog);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true);
|
program.endTransaction(id, true);
|
||||||
|
@ -725,15 +814,15 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param progsByName map of recently imported programs to be considered
|
* @param progsByName map of recently imported programs to be considered
|
||||||
* first when resolving external Libraries. Programs not saved to the project
|
* first when resolving external Libraries. Programs not saved to the project
|
||||||
* will be ignored.
|
* will be ignored.
|
||||||
* @param domainFolder the {@link DomainFolder} folder within which imported libraries will
|
* @param searchFolder the {@link DomainFolder} which imported libraries will be searched.
|
||||||
* be searched. This folder will be searched if a library is not found within the
|
* This folder will be searched if a library is not found within the list of
|
||||||
* progsByName map. If null, only progsByName will be considered.
|
* programs supplied. If null, only the list of programs will be considered.
|
||||||
* @param messageLog log for messages.
|
* @param messageLog log for messages.
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
private void resolveExternalLibraries(Program program, Map<String, Program> progsByName,
|
private void resolveExternalLibraries(Program program, Map<String, Program> progsByName,
|
||||||
DomainFolder domainFolder, TaskMonitor monitor, MessageLog messageLog)
|
DomainFolder searchFolder, TaskMonitor monitor, MessageLog messageLog)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
ExternalManager extManager = program.getExternalManager();
|
ExternalManager extManager = program.getExternalManager();
|
||||||
String[] extLibNames = extManager.getExternalLibraryNames();
|
String[] extLibNames = extManager.getExternalLibraryNames();
|
||||||
|
@ -745,8 +834,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
try {
|
try {
|
||||||
String externalFileName = FilenameUtils.getName(externalLibName);
|
String externalFileName = FilenameUtils.getName(externalLibName);
|
||||||
DomainObject matchingExtProgram =
|
DomainObject matchingExtProgram = findLibrary(progsByName, externalFileName);
|
||||||
findLibraryWithCaseCorrectSearch(progsByName, externalFileName);
|
|
||||||
if (matchingExtProgram != null && matchingExtProgram.getDomainFile().exists()) {
|
if (matchingExtProgram != null && matchingExtProgram.getDomainFile().exists()) {
|
||||||
extManager.setExternalPath(externalLibName,
|
extManager.setExternalPath(externalLibName,
|
||||||
matchingExtProgram.getDomainFile().getPathname(), false);
|
matchingExtProgram.getDomainFile().getPathname(), false);
|
||||||
|
@ -754,8 +842,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
matchingExtProgram.getDomainFile().getPathname() + "]");
|
matchingExtProgram.getDomainFile().getPathname() + "]");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DomainFile alreadyImportedLib =
|
DomainFile alreadyImportedLib = findLibrary(externalLibName, searchFolder);
|
||||||
findLibrary(externalLibName, domainFolder);
|
|
||||||
if (alreadyImportedLib != null) {
|
if (alreadyImportedLib != null) {
|
||||||
extManager.setExternalPath(externalLibName,
|
extManager.setExternalPath(externalLibName,
|
||||||
alreadyImportedLib.getPathname(), false);
|
alreadyImportedLib.getPathname(), false);
|
||||||
|
@ -806,29 +893,32 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
private List<String> getLibrarySearchPaths(ByteProvider provider, List<Option> options) {
|
private List<String> getLibrarySearchPaths(ByteProvider provider, List<Option> options) {
|
||||||
String parent = getProviderFilePath(provider);
|
String parent = getProviderFilePath(provider);
|
||||||
List<String> paths = new ArrayList<>();
|
List<String> paths = new ArrayList<>();
|
||||||
if (shouldSearchAllPaths() || isLoadLocalLibraries(options) && parent != null) {
|
if (shouldSearchAllPaths(options) || isLoadLocalLibraries(options) && parent != null) {
|
||||||
paths.add(parent);
|
paths.add(parent);
|
||||||
}
|
}
|
||||||
if (shouldSearchAllPaths() || isLoadSystemLibraries(options)) {
|
if (shouldSearchAllPaths(options) || isLoadSystemLibraries(options)) {
|
||||||
paths.addAll(LibrarySearchPathManager.getLibraryPathsList());
|
paths.addAll(LibrarySearchPathManager.getLibraryPathsList());
|
||||||
}
|
}
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up a library in the given {@link Program} map using the appropriate case comparisons
|
* Find the library within the given {@link Map} of {@link Program}s
|
||||||
*
|
*
|
||||||
* @param programsByName The map to search
|
* @param programsByName The map to search
|
||||||
* @param libraryName The library name to lookup
|
* @param libraryName The library name to lookup
|
||||||
* @return A {@link Program} that matches the given library name using appropriate case
|
* @return The found {@link Program} or null if not found
|
||||||
* comparisons, or null if one was not found
|
|
||||||
*/
|
*/
|
||||||
private Program findLibraryWithCaseCorrectSearch(Map<String, Program> programsByName,
|
private Program findLibrary(Map<String, Program> programsByName, String libraryName) {
|
||||||
String libraryName) {
|
|
||||||
Comparator<String> comparator = getLibraryNameComparator();
|
Comparator<String> comparator = getLibraryNameComparator();
|
||||||
for (String s : programsByName.keySet()) {
|
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
||||||
if (comparator.compare(libraryName, s) == 0) {
|
for (String key : programsByName.keySet()) {
|
||||||
return programsByName.get(s);
|
String candidateName = key;
|
||||||
|
if (isOptionalLibraryFilenameExtensions() && noExtension) {
|
||||||
|
candidateName = FilenameUtils.getBaseName(candidateName);
|
||||||
|
}
|
||||||
|
if (comparator.compare(candidateName, libraryName) == 0) {
|
||||||
|
return programsByName.get(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.util.List;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
|
@ -69,13 +68,8 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldLoadLibraries(List<Option> options) {
|
protected boolean shouldSearchAllPaths(List<Option> options) {
|
||||||
return shouldPerformOrdinalLookup(options) || super.shouldLoadLibraries(options);
|
return shouldPerformOrdinalLookup(options);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean shouldSearchAllPaths() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -122,15 +116,15 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Program> loadedPrograms, DomainFolder folder,
|
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
|
||||||
monitor.initialize(loadedPrograms.size());
|
monitor.initialize(loadedPrograms.size());
|
||||||
|
|
||||||
if (shouldPerformOrdinalLookup(options)) {
|
if (shouldPerformOrdinalLookup(options)) {
|
||||||
for (Program p : loadedPrograms) {
|
for (LoadedProgram loadedProgram : loadedPrograms) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
Program p = loadedProgram.program();
|
||||||
int id = p.startTransaction("Ordinal fixups");
|
int id = p.startTransaction("Ordinal fixups");
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
@ -148,7 +142,7 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
}
|
}
|
||||||
LibraryLookupTable.cleanup();
|
LibraryLookupTable.cleanup();
|
||||||
|
|
||||||
super.postLoadProgramFixups(loadedPrograms, folder, options, messageLog, monitor);
|
super.postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.opinion;
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
|
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
|
@ -57,6 +56,14 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
public static final String APPLY_LABELS_OPTION_NAME = "Apply Processor Defined Labels";
|
public static final String APPLY_LABELS_OPTION_NAME = "Apply Processor Defined Labels";
|
||||||
public static final String ANCHOR_LABELS_OPTION_NAME = "Anchor Processor Defined Labels";
|
public static final String ANCHOR_LABELS_OPTION_NAME = "Anchor Processor Defined Labels";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Program} with its associated {@link DomainFolder destination folder}
|
||||||
|
*
|
||||||
|
* @param program The {@link Program}
|
||||||
|
* @param destinationFolder The {@link DomainFolder} where the program will get loaded to
|
||||||
|
*/
|
||||||
|
public record LoadedProgram(Program program, DomainFolder destinationFolder) {/**/}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads program bytes in a particular format as a new {@link Program}. Multiple
|
* Loads program bytes in a particular format as a new {@link Program}. Multiple
|
||||||
* {@link Program}s may end up getting created, depending on the nature of the format.
|
* {@link Program}s may end up getting created, depending on the nature of the format.
|
||||||
|
@ -70,12 +77,12 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
* @param log The message log.
|
* @param log The message log.
|
||||||
* @param consumer A consumer object for {@link Program}s generated.
|
* @param consumer A consumer object for {@link Program}s generated.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A cancelable task monitor.
|
||||||
* @return A list of loaded {@link Program}s (element 0 corresponds to primary loaded
|
* @return A list of {@link LoadedProgram loaded programs} (element 0 corresponds to primary
|
||||||
* {@link Program}).
|
* loaded {@link Program}).
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
protected abstract List<Program> loadProgram(ByteProvider provider, String programName,
|
protected abstract List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException;
|
Object consumer, TaskMonitor monitor) throws IOException, CancelledException;
|
||||||
|
|
||||||
|
@ -115,54 +122,56 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Program> programs =
|
List<LoadedProgram> loadedPrograms =
|
||||||
loadProgram(provider, name, folder, loadSpec, options, messageLog, consumer, monitor);
|
loadProgram(provider, name, folder, loadSpec, options, messageLog, consumer, monitor);
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
List<Program> programsToFixup = new ArrayList<>();
|
List<LoadedProgram> programsToFixup = new ArrayList<>();
|
||||||
for (Program loadedProgram : programs) {
|
for (LoadedProgram loadedProgram : loadedPrograms) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
applyProcessorLabels(options, loadedProgram);
|
Program program = loadedProgram.program();
|
||||||
|
|
||||||
loadedProgram.setEventsEnabled(true);
|
applyProcessorLabels(options, program);
|
||||||
|
|
||||||
|
program.setEventsEnabled(true);
|
||||||
|
|
||||||
// TODO: null should not be used as a determinant for saving; don't allow null
|
// TODO: null should not be used as a determinant for saving; don't allow null
|
||||||
// folders?
|
// folders?
|
||||||
if (folder == null) {
|
if (loadedProgram.destinationFolder() == null) {
|
||||||
results.add(loadedProgram);
|
results.add(program);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String domainFileName = loadedProgram.getName();
|
String domainFileName = program.getName();
|
||||||
if (isOverrideMainProgramName()) {
|
if (isOverrideMainProgramName()) {
|
||||||
// If this is the main imported program, use the given name, otherwise, use the
|
// If this is the main imported program, use the given name, otherwise, use the
|
||||||
// internal program name. The first program in the list is the main imported program
|
// internal program name. The first program in the list is the main imported program
|
||||||
if (loadedProgram == programs.get(0)) {
|
if (program == loadedPrograms.get(0).program()) {
|
||||||
domainFileName = name;
|
domainFileName = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createProgramFile(loadedProgram, folder, domainFileName, messageLog,
|
if (createProgramFile(program, loadedProgram.destinationFolder(), domainFileName,
|
||||||
monitor)) {
|
messageLog, monitor)) {
|
||||||
results.add(loadedProgram);
|
results.add(program);
|
||||||
programsToFixup.add(loadedProgram);
|
programsToFixup.add(loadedProgram);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loadedProgram.release(consumer); // some kind of exception happened; see MessageLog
|
program.release(consumer); // some kind of exception happened; see MessageLog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subclasses can perform custom post-load fix-ups
|
// Subclasses can perform custom post-load fix-ups
|
||||||
postLoadProgramFixups(programsToFixup, folder, options, messageLog, monitor);
|
postLoadProgramFixups(programsToFixup, options, messageLog, monitor);
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(programs, consumer);
|
release(loadedPrograms, consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,20 +240,18 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This gets called after the given list of {@link Program}s is finished loading. It provides
|
* This gets called after the given list of {@link LoadedProgram programs}s is finished loading.
|
||||||
* subclasses an opportunity to do follow-on actions to the load.
|
* It provides subclasses an opportunity to do follow-on actions to the load.
|
||||||
*
|
*
|
||||||
* @param loadedPrograms The {@link Program}s that got loaded.
|
* @param loadedPrograms The {@link LoadedProgram programs} that got loaded.
|
||||||
* @param folder The folder the programs were loaded to.
|
|
||||||
* @param options The load options.
|
* @param options The load options.
|
||||||
* @param messageLog The message log.
|
* @param messageLog The message log.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A cancelable task monitor.
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
protected void postLoadProgramFixups(List<Program> loadedPrograms, DomainFolder folder,
|
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
|
||||||
// Default behavior is to do nothing.
|
// Default behavior is to do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,14 +456,14 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases the given consumer from each of the provided {@link DomainObject}s.
|
* Releases the given consumer from each of the provided {@link LoadedProgram}s.
|
||||||
*
|
*
|
||||||
* @param domainObjects A list of {@link DomainObject}s which are no longer being used.
|
* @param loadedPrograms A list of {@link LoadedProgram}s which are no longer being used.
|
||||||
* @param consumer The consumer that was marking the {@link DomainObject}s as being used.
|
* @param consumer The consumer that was marking the {@link DomainObject}s as being used.
|
||||||
*/
|
*/
|
||||||
protected final void release(List<? extends DomainObject> domainObjects, Object consumer) {
|
protected final void release(List<LoadedProgram> loadedPrograms, Object consumer) {
|
||||||
for (DomainObject dobj : domainObjects) {
|
for (LoadedProgram loadedProgram : loadedPrograms) {
|
||||||
dobj.release(consumer);
|
loadedProgram.program().release(consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -269,7 +269,7 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
|
@ -294,9 +294,9 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
prog = null;
|
prog = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Program> results = new ArrayList<Program>();
|
List<LoadedProgram> results = new ArrayList<>();
|
||||||
if (prog != null) {
|
if (prog != null) {
|
||||||
results.add(prog);
|
results.add(new LoadedProgram(prog, programFolder));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.bin.format.elf.ElfException;
|
import ghidra.app.util.bin.format.elf.ElfException;
|
||||||
import ghidra.app.util.bin.format.elf.ElfHeader;
|
import ghidra.app.util.bin.format.elf.ElfHeader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.lang.Endian;
|
import ghidra.program.model.lang.Endian;
|
||||||
|
@ -153,13 +152,12 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Program> importedPrograms, DomainFolder importFolder,
|
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
super.postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
|
||||||
super.postLoadProgramFixups(importedPrograms, importFolder, options, messageLog, monitor);
|
|
||||||
|
|
||||||
for (Program importedProgram : importedPrograms) {
|
for (LoadedProgram loadedProgram : loadedPrograms) {
|
||||||
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(importedProgram, true,
|
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedProgram.program(), true,
|
||||||
messageLog, monitor);
|
messageLog, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
|
@ -165,9 +165,9 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
||||||
prog = null;
|
prog = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Program> results = new ArrayList<Program>();
|
List<LoadedProgram> results = new ArrayList<>();
|
||||||
if (prog != null) {
|
if (prog != null) {
|
||||||
results.add(prog);
|
results.add(new LoadedProgram(prog, programFolder));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
|
@ -183,9 +183,9 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
||||||
prog = null;
|
prog = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Program> results = new ArrayList<Program>();
|
List<LoadedProgram> results = new ArrayList<>();
|
||||||
if (prog != null) {
|
if (prog != null) {
|
||||||
results.add(prog);
|
results.add(new LoadedProgram(prog, programFolder));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,10 +177,10 @@ public class XmlLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
List<Program> results = new ArrayList<>();
|
List<LoadedProgram> results = new ArrayList<>();
|
||||||
|
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||||
|
@ -212,7 +212,7 @@ public class XmlLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prog != null) {
|
if (prog != null) {
|
||||||
results.add(prog);
|
results.add(new LoadedProgram(prog, programFolder));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,12 +64,12 @@ public class ApkLoader extends DexLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
List<Program> programList = new ArrayList<>();
|
List<LoadedProgram> allLoadedPrograms = new ArrayList<>();
|
||||||
int dexIndex = 1;//DEX file numbering starts at 1
|
int dexIndex = 1;//DEX file numbering starts at 1
|
||||||
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
|
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
|
||||||
while (!monitor.isCancelled()) {
|
while (!monitor.isCancelled()) {
|
||||||
|
@ -86,11 +86,11 @@ public class ApkLoader extends DexLoader {
|
||||||
try (ByteProvider dexProvider =
|
try (ByteProvider dexProvider =
|
||||||
zipFS.getByteProvider(classesDexFile, monitor)) {
|
zipFS.getByteProvider(classesDexFile, monitor)) {
|
||||||
// defer to the super class (DexLoader) to actually load the DEX file
|
// defer to the super class (DexLoader) to actually load the DEX file
|
||||||
List<Program> program =
|
List<LoadedProgram> loadedPrograms =
|
||||||
super.loadProgram(dexProvider, classesDexFile.getName(), programFolder,
|
super.loadProgram(dexProvider, classesDexFile.getName(), programFolder,
|
||||||
loadSpec, options, log, consumer, monitor);
|
loadSpec, options, log, consumer, monitor);
|
||||||
|
|
||||||
programList.addAll(program);
|
allLoadedPrograms.addAll(loadedPrograms);
|
||||||
}
|
}
|
||||||
++dexIndex;
|
++dexIndex;
|
||||||
}
|
}
|
||||||
|
@ -101,11 +101,11 @@ public class ApkLoader extends DexLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(programList, consumer);
|
release(allLoadedPrograms, consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
link(programList, log, monitor);
|
link(allLoadedPrograms.stream().map(e -> e.program()).toList(), log, monitor);
|
||||||
return programList;
|
return allLoadedPrograms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -590,9 +590,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
<UL>
|
<UL>
|
||||||
<LI><typewriter>-loader-applyLabels <true|false></typewriter></LI>
|
<LI><typewriter>-loader-applyLabels <true|false></typewriter></LI>
|
||||||
<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-projectLibrarySearchFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadSystemLibraries <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-applyRelocations <true|false></typewriter></LI>
|
<LI><typewriter>-loader-applyRelocations <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-imagebase <imagebase<sup>3</sup>></typewriter></LI>
|
<LI><typewriter>-loader-imagebase <imagebase<sup>3</sup>></typewriter></LI>
|
||||||
<LI><typewriter>-loader-dataImageBase <dataImageBase<sup>4</sup>></typewriter></LI>
|
<LI><typewriter>-loader-dataImageBase <dataImageBase<sup>4</sup>></typewriter></LI>
|
||||||
|
@ -602,9 +605,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
<UL>
|
<UL>
|
||||||
<LI><typewriter>-loader-applyLabels <true|false></typewriter></LI>
|
<LI><typewriter>-loader-applyLabels <true|false></typewriter></LI>
|
||||||
<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-projectLibrarySearchFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadSystemLibraries <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-ordinalLookup <true|false></typewriter></LI>
|
<LI><typewriter>-loader-ordinalLookup <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-parseCliHeaders <true|false></typewriter></LI>
|
<LI><typewriter>-loader-parseCliHeaders <true|false></typewriter></LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
@ -612,9 +618,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
|
||||||
<UL>
|
<UL>
|
||||||
<LI><typewriter>-loader-applyLabels <true|false></typewriter></LI>
|
<LI><typewriter>-loader-applyLabels <true|false></typewriter></LI>
|
||||||
<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-projectLibrarySearchFolder <project path></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
<LI><typewriter>-loader-loadLocalLibraries <true|false></typewriter></LI>
|
||||||
<LI><typewriter>-loader-loadSystemLibraries <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-addChainedFixupsRelocations <true|false></typewriter></LI>
|
<LI><typewriter>-loader-addChainedFixupsRelocations <true|false></typewriter></LI>
|
||||||
</UL>
|
</UL>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
Loading…
Reference in a new issue