GP-2604: More load library options

This commit is contained in:
Ryan Kurtz 2022-09-23 05:15:36 -04:00
parent 6d6491905b
commit fdda6b672e
12 changed files with 294 additions and 158 deletions

View file

@ -29,38 +29,27 @@
<BLOCKQUOTE>
<UL>
<LI>Android APK</LI>
<LI>Common Object File Format (COFF)</LI>
<LI>Dalvik Executable (DEX)</LI>
<LI>Debug Symbols (DBG)</LI>
<LI>Dump File Loader</LI>
<LI>DYLD Shared Cache</LI>
<LI>Executable and Linking Format (ELF)</LI>
<LI>Ghidra Data Type Archive Format</LI>
<LI>GZF Input Format</LI>
<LI>Intel Hex</LI>
<LI>Java Class File</LI>
<LI>Mac OS X Mach-O</LI>
<LI>Module Definition (DEF)</LI>
<LI>Motorola Hex</LI>
<LI>New Executable (NE)</LI>
<LI>Old-style DOS Executable (MZ)</LI>
<LI>Portable Executable (PE)</LI>
<LI>Preferred Executable Format (PEF)</LI>
<LI>Program Mapfile (MAP)</LI>
<LI>Raw Binary</LI>
<LI>Relocatable Object Module Format (OMF)</LI>
<LI>XML Input Format</LI>
</UL>
</BLOCKQUOTE>
@ -281,8 +270,22 @@
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>
</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>
<P>Searches the executable's directory to recursively resolve the external libraries used
@ -292,7 +295,7 @@
in these programs will be resolved.<BR>
</BLOCKQUOTE>
<H4>Load System Libraries</H4>
<H4>Load System Libraries From Disk</H4>
<BLOCKQUOTE>
<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
traversed when loading local or system libraries.</P>
</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>
<H3>COFF Options<A name="Options_Common Object_File_Format__COFF_"/></H3>

View file

@ -28,12 +28,15 @@ import javax.swing.event.DocumentListener;
import org.apache.commons.collections4.map.LazyMap;
import docking.DockingWindowManager;
import docking.options.editor.ButtonPanelFactory;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GComboBox;
import docking.widgets.label.GLabel;
import docking.widgets.textfield.IntegerTextField;
import ghidra.app.util.opinion.AbstractLibrarySupportLoader;
import ghidra.app.util.opinion.LibraryPathsDialog;
import ghidra.framework.main.DataTreeDialog;
import ghidra.framework.model.DomainFolder;
import ghidra.program.model.address.*;
import ghidra.util.exception.AssertException;
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.
*/
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 int columns;
private AddressFactoryService addressFactoryService;
@ -177,9 +180,13 @@ public class OptionsEditorPanel extends JPanel {
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)) {
return buildLoadLibraryPathsEditor(option);
return buildPathsEditor(option);
}
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());
JButton button = new JButton("Edit Paths");
button.addActionListener(
@ -233,6 +240,27 @@ public class OptionsEditorPanel extends JPanel {
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) {
JComboBox<AddressSpace> combo = new GComboBox<>();
AddressFactory addressFactory = addressFactoryService.getAddressFactory();

View file

@ -50,15 +50,24 @@ import utilities.util.FileUtilities;
*/
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;
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;
public static final String DEPTH_OPTION_NAME = "Recursive Library Load Depth";
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}.
*
@ -76,11 +85,11 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
throws CancelledException, IOException;
@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,
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
List<Program> programList = new ArrayList<>();
List<LoadedProgram> loadedProgramList = new ArrayList<>();
List<String> libraryNameList = new ArrayList<>();
boolean success = false;
@ -88,21 +97,19 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
// Load the primary program
Program program = doLoad(provider, programName, programFolder, loadSpec,
libraryNameList, options, consumer, log, monitor);
programList.add(program);
loadedProgramList.add(new LoadedProgram(program, programFolder));
// Load the libraries, if applicable
if (shouldLoadLibraries(options)) {
List<Program> libraries = loadLibraries(provider, program, programFolder, loadSpec,
options, log, consumer, libraryNameList, monitor);
programList.addAll(libraries);
}
// Load the libraries
List<LoadedProgram> libraries = loadLibraries(provider, program, programFolder,
loadSpec, options, log, consumer, libraryNameList, monitor);
loadedProgramList.addAll(libraries);
success = true;
return programList;
return loadedProgramList;
}
finally {
if (!success) {
release(programList, consumer);
release(loadedProgramList, consumer);
}
}
}
@ -126,11 +133,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
}
@Override
protected void postLoadProgramFixups(List<Program> loadedPrograms, DomainFolder folder,
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
if (isLoadLocalLibraries(options) || isLoadSystemLibraries(options)) {
fixupExternalLibraries(loadedPrograms, folder, true, messageLog, monitor);
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
if (isLinkExistingLibraries(options) || isLoadLocalLibraries(options) ||
isLoadSystemLibraries(options)) {
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) {
List<Option> list =
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,
Loader.COMMAND_LINE_ARG_PREFIX + "-loadLocalLibraries"));
list.add(new Option(SYSTEM_LIBRARY_OPTION_NAME, SYSTEM_LIBRARY_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-loadSystemLibraries"));
list.add(new Option(DEPTH_OPTION_NAME, DEPTH_OPTION_DEFAULT, Integer.class,
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;
}
@ -164,7 +180,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
if (options != null) {
for (Option option : options) {
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())) {
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();
}
}
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);
}
/**
* 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
* 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
* @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) {
return (isLoadLocalLibraries(options) || isLoadSystemLibraries(options)) &&
getLibraryLoadDepth(options) > 0;
protected DomainFolder getLibraryDestinationFolder(DomainFolder programFolder,
List<Option> options) {
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
* 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
*/
protected boolean shouldSearchAllPaths() {
protected boolean shouldSearchAllPaths(List<Option> options) {
return false;
}
@ -340,7 +429,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
protected boolean processLibrary(Program library, String libraryName, File libraryFile,
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
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 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,
MessageLog log, Object consumer, List<String> libraryNameList, TaskMonitor monitor)
throws CancelledException, IOException {
List<Program> programList = new ArrayList<>();
List<LoadedProgram> loadedPrograms = new ArrayList<>();
Set<String> processed = new HashSet<>();
Queue<UnprocessedLibrary> unprocessed =
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
List<String> searchPaths = getLibrarySearchPaths(provider, options);
DomainFolder linkSearchFolder = getLinkSearchFolder(programFolder, options);
DomainFolder libraryDestFolder = getLibraryDestinationFolder(programFolder, options);
while (!unprocessed.isEmpty()) {
monitor.checkCanceled();
@ -380,8 +471,12 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
if (depth == 0 || processed.contains(libraryName)) {
continue;
}
processed.add(libraryName);
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);
List<File> candidateLibraryFiles =
@ -399,7 +494,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
foundLibrary = true;
if (processLibrary(library, libraryName, candidateLibraryFile, provider,
desiredLoadSpec, options, log, monitor)) {
programList.add(library);
loadedPrograms.add(new LoadedProgram(library, libraryDestFolder));
log.appendMsg(
"Library " + libraryName + ": Saving " + candidateLibraryFile);
}
@ -415,14 +510,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
log.appendMsg("Library " + libraryName + ": Not found");
}
}
else {
log.appendMsg("Library " + libraryName + ": Already loaded ");
}
processed.add(libraryName);
}
log.appendMsg(
"Finished importing referenced libraries for: " + program.getName());
return programList;
return loadedPrograms;
}
/**
@ -548,7 +637,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* a {@link ByteProvider} available.
*
* @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
* to this folder yet, that is handled in a later follow on step.
* @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 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,
Object consumer, MessageLog log, TaskMonitor monitor)
throws CancelledException, IOException {
@ -579,7 +668,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
return null;
}
Program library = doLoad(provider, libraryName, programFolder, libLoadSpec,
Program library = doLoad(provider, libraryName, libraryFolder, libLoadSpec,
libraryNameList, options, consumer, log, monitor);
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
* to the project will be considered as a valid external library.
* @param domainFolder the {@link DomainFolder} folder within which imported libraries will
* be searched. This folder will be searched if a library is not found within the
* list of programs supplied. If null, only the list of programs will be considered.
* @param searchFolder the {@link DomainFolder} which imported libraries will be searched.
* This folder will be searched if a library is not found within the list of
* 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 messageLog log for messages.
* @param monitor the task monitor
* @throws IOException if there was an IO-related problem resolving.
* @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)
throws CancelledException, IOException {
@ -703,7 +792,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
monitor.setMessage("Resolving..." + program.getName());
int id = program.startTransaction("Resolving external references");
try {
resolveExternalLibraries(program, progsByName, domainFolder, monitor, messageLog);
resolveExternalLibraries(program, progsByName, searchFolder, monitor, messageLog);
}
finally {
program.endTransaction(id, true);
@ -725,15 +814,15 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* @param progsByName map of recently imported programs to be considered
* first when resolving external Libraries. Programs not saved to the project
* will be ignored.
* @param domainFolder the {@link DomainFolder} folder within which imported libraries will
* be searched. This folder will be searched if a library is not found within the
* progsByName map. If null, only progsByName will be considered.
* @param searchFolder the {@link DomainFolder} which imported libraries will be searched.
* This folder will be searched if a library is not found within the list of
* programs supplied. If null, only the list of programs will be considered.
* @param messageLog log for messages.
* @param monitor the task monitor
* @throws CancelledException if the user cancelled the load.
*/
private void resolveExternalLibraries(Program program, Map<String, Program> progsByName,
DomainFolder domainFolder, TaskMonitor monitor, MessageLog messageLog)
DomainFolder searchFolder, TaskMonitor monitor, MessageLog messageLog)
throws CancelledException {
ExternalManager extManager = program.getExternalManager();
String[] extLibNames = extManager.getExternalLibraryNames();
@ -745,8 +834,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
monitor.checkCanceled();
try {
String externalFileName = FilenameUtils.getName(externalLibName);
DomainObject matchingExtProgram =
findLibraryWithCaseCorrectSearch(progsByName, externalFileName);
DomainObject matchingExtProgram = findLibrary(progsByName, externalFileName);
if (matchingExtProgram != null && matchingExtProgram.getDomainFile().exists()) {
extManager.setExternalPath(externalLibName,
matchingExtProgram.getDomainFile().getPathname(), false);
@ -754,8 +842,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
matchingExtProgram.getDomainFile().getPathname() + "]");
}
else {
DomainFile alreadyImportedLib =
findLibrary(externalLibName, domainFolder);
DomainFile alreadyImportedLib = findLibrary(externalLibName, searchFolder);
if (alreadyImportedLib != null) {
extManager.setExternalPath(externalLibName,
alreadyImportedLib.getPathname(), false);
@ -806,29 +893,32 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
private List<String> getLibrarySearchPaths(ByteProvider provider, List<Option> options) {
String parent = getProviderFilePath(provider);
List<String> paths = new ArrayList<>();
if (shouldSearchAllPaths() || isLoadLocalLibraries(options) && parent != null) {
if (shouldSearchAllPaths(options) || isLoadLocalLibraries(options) && parent != null) {
paths.add(parent);
}
if (shouldSearchAllPaths() || isLoadSystemLibraries(options)) {
if (shouldSearchAllPaths(options) || isLoadSystemLibraries(options)) {
paths.addAll(LibrarySearchPathManager.getLibraryPathsList());
}
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 libraryName The library name to lookup
* @return A {@link Program} that matches the given library name using appropriate case
* comparisons, or null if one was not found
* @return The found {@link Program} or null if not found
*/
private Program findLibraryWithCaseCorrectSearch(Map<String, Program> programsByName,
String libraryName) {
private Program findLibrary(Map<String, Program> programsByName, String libraryName) {
Comparator<String> comparator = getLibraryNameComparator();
for (String s : programsByName.keySet()) {
if (comparator.compare(libraryName, s) == 0) {
return programsByName.get(s);
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
for (String key : programsByName.keySet()) {
String candidateName = key;
if (isOptionalLibraryFilenameExtensions() && noExtension) {
candidateName = FilenameUtils.getBaseName(candidateName);
}
if (comparator.compare(candidateName, libraryName) == 0) {
return programsByName.get(key);
}
}
return null;

View file

@ -23,7 +23,6 @@ import java.util.List;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.Options;
import ghidra.program.model.listing.Function;
@ -69,13 +68,8 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
}
@Override
protected boolean shouldLoadLibraries(List<Option> options) {
return shouldPerformOrdinalLookup(options) || super.shouldLoadLibraries(options);
}
@Override
protected boolean shouldSearchAllPaths() {
return true;
protected boolean shouldSearchAllPaths(List<Option> options) {
return shouldPerformOrdinalLookup(options);
}
@Override
@ -122,15 +116,15 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
}
@Override
protected void postLoadProgramFixups(List<Program> loadedPrograms, DomainFolder folder,
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
monitor.initialize(loadedPrograms.size());
if (shouldPerformOrdinalLookup(options)) {
for (Program p : loadedPrograms) {
for (LoadedProgram loadedProgram : loadedPrograms) {
monitor.checkCanceled();
Program p = loadedProgram.program();
int id = p.startTransaction("Ordinal fixups");
boolean success = false;
try {
@ -148,7 +142,7 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
}
LibraryLookupTable.cleanup();
super.postLoadProgramFixups(loadedPrograms, folder, options, messageLog, monitor);
super.postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
}
/**

View file

@ -15,11 +15,10 @@
*/
package ghidra.app.util.opinion;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
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 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
* {@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 consumer A consumer object for {@link Program}s generated.
* @param monitor A cancelable task monitor.
* @return A list of loaded {@link Program}s (element 0 corresponds to primary loaded
* {@link Program}).
* @return A list of {@link LoadedProgram loaded programs} (element 0 corresponds to primary
* loaded {@link Program}).
* @throws IOException if there was an IO-related problem loading.
* @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,
Object consumer, TaskMonitor monitor) throws IOException, CancelledException;
@ -115,54 +122,56 @@ public abstract class AbstractProgramLoader implements Loader {
return results;
}
List<Program> programs =
List<LoadedProgram> loadedPrograms =
loadProgram(provider, name, folder, loadSpec, options, messageLog, consumer, monitor);
boolean success = false;
try {
monitor.checkCanceled();
List<Program> programsToFixup = new ArrayList<>();
for (Program loadedProgram : programs) {
List<LoadedProgram> programsToFixup = new ArrayList<>();
for (LoadedProgram loadedProgram : loadedPrograms) {
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
// folders?
if (folder == null) {
results.add(loadedProgram);
if (loadedProgram.destinationFolder() == null) {
results.add(program);
continue;
}
String domainFileName = loadedProgram.getName();
String domainFileName = program.getName();
if (isOverrideMainProgramName()) {
// 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
if (loadedProgram == programs.get(0)) {
if (program == loadedPrograms.get(0).program()) {
domainFileName = name;
}
}
if (createProgramFile(loadedProgram, folder, domainFileName, messageLog,
monitor)) {
results.add(loadedProgram);
if (createProgramFile(program, loadedProgram.destinationFolder(), domainFileName,
messageLog, monitor)) {
results.add(program);
programsToFixup.add(loadedProgram);
}
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
postLoadProgramFixups(programsToFixup, folder, options, messageLog, monitor);
postLoadProgramFixups(programsToFixup, options, messageLog, monitor);
success = true;
}
finally {
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
* subclasses an opportunity to do follow-on actions to the load.
* This gets called after the given list of {@link LoadedProgram programs}s is finished loading.
* It provides subclasses an opportunity to do follow-on actions to the load.
*
* @param loadedPrograms The {@link Program}s that got loaded.
* @param folder The folder the programs were loaded to.
* @param loadedPrograms The {@link LoadedProgram programs} that got loaded.
* @param options The load options.
* @param messageLog The message log.
* @param monitor A cancelable task monitor.
* @throws IOException if there was an IO-related problem loading.
* @throws CancelledException if the user cancelled the load.
*/
protected void postLoadProgramFixups(List<Program> loadedPrograms, DomainFolder folder,
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
// 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.
*/
protected final void release(List<? extends DomainObject> domainObjects, Object consumer) {
for (DomainObject dobj : domainObjects) {
dobj.release(consumer);
protected final void release(List<LoadedProgram> loadedPrograms, Object consumer) {
for (LoadedProgram loadedProgram : loadedPrograms) {
loadedProgram.program().release(consumer);
}
}

View file

@ -269,7 +269,7 @@ public class BinaryLoader extends AbstractProgramLoader {
}
@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,
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
@ -294,9 +294,9 @@ public class BinaryLoader extends AbstractProgramLoader {
prog = null;
}
}
List<Program> results = new ArrayList<Program>();
List<LoadedProgram> results = new ArrayList<>();
if (prog != null) {
results.add(prog);
results.add(new LoadedProgram(prog, programFolder));
}
return results;
}

View file

@ -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.ElfHeader;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.Options;
import ghidra.program.model.lang.Endian;
@ -153,13 +152,12 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
}
@Override
protected void postLoadProgramFixups(List<Program> importedPrograms, DomainFolder importFolder,
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
super.postLoadProgramFixups(importedPrograms, importFolder, options, messageLog, monitor);
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
super.postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
for (Program importedProgram : importedPrograms) {
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(importedProgram, true,
for (LoadedProgram loadedProgram : loadedPrograms) {
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedProgram.program(), true,
messageLog, monitor);
}
}

View file

@ -142,7 +142,7 @@ public class IntelHexLoader extends AbstractProgramLoader {
}
@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,
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
@ -165,9 +165,9 @@ public class IntelHexLoader extends AbstractProgramLoader {
prog = null;
}
}
List<Program> results = new ArrayList<Program>();
List<LoadedProgram> results = new ArrayList<>();
if (prog != null) {
results.add(prog);
results.add(new LoadedProgram(prog, programFolder));
}
return results;
}

View file

@ -160,7 +160,7 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
}
@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,
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
@ -183,9 +183,9 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
prog = null;
}
}
List<Program> results = new ArrayList<Program>();
List<LoadedProgram> results = new ArrayList<>();
if (prog != null) {
results.add(prog);
results.add(new LoadedProgram(prog, programFolder));
}
return results;
}

View file

@ -177,10 +177,10 @@ public class XmlLoader extends AbstractProgramLoader {
}
@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,
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
List<Program> results = new ArrayList<>();
List<LoadedProgram> results = new ArrayList<>();
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
@ -212,7 +212,7 @@ public class XmlLoader extends AbstractProgramLoader {
}
}
if (prog != null) {
results.add(prog);
results.add(new LoadedProgram(prog, programFolder));
}
return results;
}

View file

@ -64,12 +64,12 @@ public class ApkLoader extends DexLoader {
}
@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,
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
boolean success = false;
List<Program> programList = new ArrayList<>();
List<LoadedProgram> allLoadedPrograms = new ArrayList<>();
int dexIndex = 1;//DEX file numbering starts at 1
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
while (!monitor.isCancelled()) {
@ -86,11 +86,11 @@ public class ApkLoader extends DexLoader {
try (ByteProvider dexProvider =
zipFS.getByteProvider(classesDexFile, monitor)) {
// defer to the super class (DexLoader) to actually load the DEX file
List<Program> program =
List<LoadedProgram> loadedPrograms =
super.loadProgram(dexProvider, classesDexFile.getName(), programFolder,
loadSpec, options, log, consumer, monitor);
programList.addAll(program);
allLoadedPrograms.addAll(loadedPrograms);
}
++dexIndex;
}
@ -101,11 +101,11 @@ public class ApkLoader extends DexLoader {
}
finally {
if (!success) {
release(programList, consumer);
release(allLoadedPrograms, consumer);
}
}
link(programList, log, monitor);
return programList;
link(allLoadedPrograms.stream().map(e -> e.program()).toList(), log, monitor);
return allLoadedPrograms;
}
@Override

View file

@ -590,9 +590,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
<UL>
<LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-anchorLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-linkExistingProjectLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-projectLibrarySearchFolder &lt;project path&gt;</typewriter></LI>
<LI><typewriter>-loader-loadLocalLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-loadSystemLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-libraryLoadDepth &lt;depth&gt;</typewriter></LI>
<LI><typewriter>-loader-libraryDestinationFolder &lt;project path&gt;</typewriter></LI>
<LI><typewriter>-loader-applyRelocations &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-imagebase &lt;imagebase<sup>3</sup>&gt;</typewriter></LI>
<LI><typewriter>-loader-dataImageBase &lt;dataImageBase<sup>4</sup>&gt;</typewriter></LI>
@ -602,9 +605,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
<UL>
<LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-anchorLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-linkExistingProjectLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-projectLibrarySearchFolder &lt;project path&gt;</typewriter></LI>
<LI><typewriter>-loader-loadLocalLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-loadSystemLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-libraryLoadDepth &lt;depth&gt;</typewriter></LI>
<LI><typewriter>-loader-libraryDestinationFolder &lt;project path&gt;</typewriter></LI>
<LI><typewriter>-loader-ordinalLookup &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-parseCliHeaders &lt;true|false&gt;</typewriter></LI>
</UL>
@ -612,9 +618,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
<UL>
<LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-anchorLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-linkExistingProjectLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-projectLibrarySearchFolder &lt;project path&gt;</typewriter></LI>
<LI><typewriter>-loader-loadLocalLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-loadSystemLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-libraryLoadDepth &lt;depth&gt;</typewriter></LI>
<LI><typewriter>-loader-libraryDestinationFolder &lt;project path&gt;</typewriter></LI>
<LI><typewriter>-loader-addChainedFixupsRelocations &lt;true|false&gt;</typewriter></LI>
</UL>
</UL>