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> <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>

View file

@ -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();

View file

@ -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;

View file

@ -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);
} }
/** /**

View file

@ -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);
} }
} }

View file

@ -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;
} }

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.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);
} }
} }

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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

View file

@ -590,9 +590,12 @@ The Headless Analyzer uses the command-line parameters discussed below. See <a h
<UL> <UL>
<LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI> <LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-anchorLabels &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-loadLocalLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-loadSystemLibraries &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-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-applyRelocations &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-imagebase &lt;imagebase<sup>3</sup>&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> <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> <UL>
<LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI> <LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-anchorLabels &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-loadLocalLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-loadSystemLibraries &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-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-ordinalLookup &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-parseCliHeaders &lt;true|false&gt;</typewriter></LI> <LI><typewriter>-loader-parseCliHeaders &lt;true|false&gt;</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 &lt;true|false&gt;</typewriter></LI> <LI><typewriter>-loader-applyLabels &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-anchorLabels &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-loadLocalLibraries &lt;true|false&gt;</typewriter></LI>
<LI><typewriter>-loader-loadSystemLibraries &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-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> <LI><typewriter>-loader-addChainedFixupsRelocations &lt;true|false&gt;</typewriter></LI>
</UL> </UL>
</UL> </UL>