mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-5545_5600: ProgramLoader and more flexible loader args
This commit is contained in:
parent
4aa78ae6d0
commit
d3aed2c4b3
47 changed files with 2026 additions and 976 deletions
|
@ -20,8 +20,8 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.opinion.LoadResults;
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.DomainFolder;
|
||||||
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||||
|
@ -53,21 +53,20 @@ public class ImportAllProgramsFromADirectoryScript extends GhidraScript {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadResults<Program> loadResults = null;
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
try {
|
.source(file)
|
||||||
loadResults = AutoImporter.importByLookingForLcs(file, state.getProject(),
|
.project(state.getProject())
|
||||||
folder.getPathname(), language.getLanguage(), language.getCompilerSpec(), this,
|
.projectFolderPath(folder.getPathname())
|
||||||
log, monitor);
|
.language(language.getLanguage())
|
||||||
loadResults.getPrimary().save(state.getProject(), log, monitor);
|
.compiler(language.getCompilerSpec())
|
||||||
|
.log(log)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load()) {
|
||||||
|
loadResults.getPrimary().save(monitor);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
println("Unable to import program from file " + file.getName());
|
println("Unable to import program from file " + file.getName());
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
if (loadResults != null) {
|
|
||||||
loadResults.release(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println(log.toString());
|
println(log.toString());
|
||||||
log.clear();
|
log.clear();
|
||||||
|
|
|
@ -43,8 +43,7 @@ import ghidra.app.tablechooser.TableChooserExecutor;
|
||||||
import ghidra.app.util.demangler.DemangledObject;
|
import ghidra.app.util.demangler.DemangledObject;
|
||||||
import ghidra.app.util.demangler.DemanglerUtil;
|
import ghidra.app.util.demangler.DemanglerUtil;
|
||||||
import ghidra.app.util.dialog.AskAddrDialog;
|
import ghidra.app.util.dialog.AskAddrDialog;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.query.TableService;
|
import ghidra.app.util.query.TableService;
|
||||||
import ghidra.app.util.viewer.field.BrowserCodeUnitFormat;
|
import ghidra.app.util.viewer.field.BrowserCodeUnitFormat;
|
||||||
|
@ -3620,8 +3619,8 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
/**
|
/**
|
||||||
* Attempts to import the specified file. It attempts to detect the format and
|
* Attempts to import the specified file. It attempts to detect the format and
|
||||||
* automatically import the file. If the format is unable to be determined, then
|
* automatically import the file. If the format is unable to be determined, then
|
||||||
* null is returned. For more control over the import process, {@link AutoImporter} may be
|
* null is returned. For more control over the import process, {@link ProgramLoader} may be
|
||||||
* directly called.
|
* directly used.
|
||||||
* <p>
|
* <p>
|
||||||
* NOTE: The returned {@link Program} is not automatically saved into the current project.
|
* NOTE: The returned {@link Program} is not automatically saved into the current project.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -3634,11 +3633,12 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
* @throws Exception if any exceptions occur while importing
|
* @throws Exception if any exceptions occur while importing
|
||||||
*/
|
*/
|
||||||
public Program importFile(File file) throws Exception {
|
public Program importFile(File file) throws Exception {
|
||||||
try {
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
LoadResults<Program> loadResults = AutoImporter.importByUsingBestGuess(file,
|
.source(file)
|
||||||
state.getProject(), null, this, new MessageLog(), monitor);
|
.project(state.getProject())
|
||||||
loadResults.releaseNonPrimary(this);
|
.monitor(monitor)
|
||||||
return loadResults.getPrimaryDomainObject();
|
.load()) {
|
||||||
|
return loadResults.getPrimaryDomainObject(this);
|
||||||
}
|
}
|
||||||
catch (LoadException e) {
|
catch (LoadException e) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -3647,7 +3647,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports the specified file as raw binary. For more control over the import process,
|
* Imports the specified file as raw binary. For more control over the import process,
|
||||||
* {@link AutoImporter} may be directly called.
|
* {@link ProgramLoader} may be directly used.
|
||||||
* <p>
|
* <p>
|
||||||
* NOTE: It is the responsibility of the script that calls this method to release the returned
|
* NOTE: It is the responsibility of the script that calls this method to release the returned
|
||||||
* {@link Program} with {@link DomainObject#release(Object consumer)} when it is no longer
|
* {@link Program} with {@link DomainObject#release(Object consumer)} when it is no longer
|
||||||
|
@ -3661,10 +3661,15 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
*/
|
*/
|
||||||
public Program importFileAsBinary(File file, Language language, CompilerSpec compilerSpec)
|
public Program importFileAsBinary(File file, Language language, CompilerSpec compilerSpec)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try {
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
Loaded<Program> loaded = AutoImporter.importAsBinary(file, state.getProject(), null,
|
.source(file)
|
||||||
language, compilerSpec, this, new MessageLog(), monitor);
|
.project(state.getProject())
|
||||||
return loaded.getDomainObject();
|
.loaders(BinaryLoader.class)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load()) {
|
||||||
|
return loadResults.getPrimaryDomainObject(this);
|
||||||
}
|
}
|
||||||
catch (LoadException e) {
|
catch (LoadException e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -45,8 +45,8 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
IMPORT("-import", true, "[<directory>|<file>]+"),
|
IMPORT("-import", true, "[<directory>|<file>]+"),
|
||||||
PROCESS("-process", true, "[<project_file>]"),
|
PROCESS("-process", true, "[<project_file>]"),
|
||||||
PRE_SCRIPT("-prescript", true, "<ScriptName>"),
|
PRE_SCRIPT("-preScript", true, "<ScriptName>"),
|
||||||
POST_SCRIPT("-postscript", true, "<ScriptName>"),
|
POST_SCRIPT("-postScript", true, "<ScriptName>"),
|
||||||
SCRIPT_PATH("-scriptPath", true, "\"<path1>[;<path2>...]\""),
|
SCRIPT_PATH("-scriptPath", true, "\"<path1>[;<path2>...]\""),
|
||||||
PROPERTIES_PATH("-propertiesPath", true, "\"<path1>[;<path2>...]\""),
|
PROPERTIES_PATH("-propertiesPath", true, "\"<path1>[;<path2>...]\""),
|
||||||
SCRIPT_LOG("-scriptlog", true, "<path to script log file>"),
|
SCRIPT_LOG("-scriptlog", true, "<path to script log file>"),
|
||||||
|
@ -54,7 +54,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
||||||
OVERWRITE("-overwrite", false),
|
OVERWRITE("-overwrite", false),
|
||||||
RECURSIVE("-recursive", false),
|
RECURSIVE("-recursive", false),
|
||||||
READ_ONLY("-readOnly", false),
|
READ_ONLY("-readOnly", false),
|
||||||
DELETE_PROJECT("-deleteproject", false),
|
DELETE_PROJECT("-deleteProject", false),
|
||||||
NO_ANALYSIS("-noanalysis", false),
|
NO_ANALYSIS("-noanalysis", false),
|
||||||
PROCESSOR("-processor", true, "<languageID>"),
|
PROCESSOR("-processor", true, "<languageID>"),
|
||||||
CSPEC("-cspec", true, "<compilerSpecID>"),
|
CSPEC("-cspec", true, "<compilerSpecID>"),
|
||||||
|
@ -66,7 +66,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
||||||
OK_TO_DELETE("-okToDelete", false),
|
OK_TO_DELETE("-okToDelete", false),
|
||||||
MAX_CPU("-max-cpu", true, "<max cpu cores to use>"),
|
MAX_CPU("-max-cpu", true, "<max cpu cores to use>"),
|
||||||
LIBRARY_SEARCH_PATHS("-librarySearchPaths", true, "<path1>[;<path2>...]"),
|
LIBRARY_SEARCH_PATHS("-librarySearchPaths", true, "<path1>[;<path2>...]"),
|
||||||
LOADER("-loader", true, "<desired loader name>"),
|
LOADER(Loader.COMMAND_LINE_ARG_PREFIX, true, "<desired loader name>"),
|
||||||
LOADER_ARGS(Loader.COMMAND_LINE_ARG_PREFIX + "-", true, "<loader argument value>") {
|
LOADER_ARGS(Loader.COMMAND_LINE_ARG_PREFIX + "-", true, "<loader argument value>") {
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(String arg) {
|
public boolean matches(String arg) {
|
||||||
|
@ -424,7 +424,8 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
||||||
options.setPostScriptsWithArgs(postScripts);
|
options.setPostScriptsWithArgs(postScripts);
|
||||||
|
|
||||||
// Set loader and loader args
|
// Set loader and loader args
|
||||||
options.setLoader(loaderName, loaderArgs);
|
options.setLoader(loaderName);
|
||||||
|
options.setLoaderArgs(loaderArgs);
|
||||||
|
|
||||||
// Set user-specified language and compiler spec
|
// Set user-specified language and compiler spec
|
||||||
options.setLanguageAndCompiler(languageId, compilerSpecId);
|
options.setLanguageAndCompiler(languageId, compilerSpecId);
|
||||||
|
|
|
@ -31,8 +31,7 @@ import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||||
import ghidra.app.script.*;
|
import ghidra.app.script.*;
|
||||||
import ghidra.app.util.headless.HeadlessScript.HeadlessContinuationOption;
|
import ghidra.app.util.headless.HeadlessScript.HeadlessContinuationOption;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.framework.*;
|
import ghidra.framework.*;
|
||||||
|
@ -1529,17 +1528,23 @@ public class HeadlessAnalyzer {
|
||||||
|
|
||||||
Msg.info(this, "IMPORTING: " + fsrl);
|
Msg.info(this, "IMPORTING: " + fsrl);
|
||||||
|
|
||||||
LoadResults<Program> loadResults = null;
|
// Perform the load. Note that loading 1 file may result in more than 1 thing getting
|
||||||
Loaded<Program> primary = null;
|
// loaded.
|
||||||
try {
|
Program primaryProgram = null;
|
||||||
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
|
.source(fsrl)
|
||||||
|
.project(project)
|
||||||
|
.projectFolderPath(folderPath)
|
||||||
|
.language(options.language)
|
||||||
|
.compiler(options.compilerSpec)
|
||||||
|
.loaders(options.loaderClass)
|
||||||
|
.loaderArgs(options.loaderArgs)
|
||||||
|
.load()) {
|
||||||
|
|
||||||
// Perform the load. Note that loading 1 file may result in more than 1 thing getting
|
|
||||||
// loaded.
|
|
||||||
loadResults = loadPrograms(fsrl, folderPath);
|
|
||||||
Msg.info(this, "IMPORTING: Loaded " + (loadResults.size() - 1) + " additional files");
|
Msg.info(this, "IMPORTING: Loaded " + (loadResults.size() - 1) + " additional files");
|
||||||
|
|
||||||
primary = loadResults.getPrimary();
|
Loaded<Program> primary = loadResults.getPrimary();
|
||||||
Program primaryProgram = primary.getDomainObject();
|
primaryProgram = primary.getDomainObject(this);
|
||||||
|
|
||||||
// Make sure we are allowed to save ALL programs to the project. If not, save none and
|
// Make sure we are allowed to save ALL programs to the project. If not, save none and
|
||||||
// fail.
|
// fail.
|
||||||
|
@ -1565,7 +1570,15 @@ public class HeadlessAnalyzer {
|
||||||
// The act of marking the program as temporary by a script will signal
|
// The act of marking the program as temporary by a script will signal
|
||||||
// us to discard any changes
|
// us to discard any changes
|
||||||
if (!doSave) {
|
if (!doSave) {
|
||||||
loadResults.forEach(e -> e.getDomainObject().setTemporary(true));
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
|
Program program = loaded.getDomainObject(this);
|
||||||
|
try {
|
||||||
|
program.setTemporary(true);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply saveDomainFolder to the primary program, if applicable.
|
// Apply saveDomainFolder to the primary program, if applicable.
|
||||||
|
@ -1581,38 +1594,48 @@ public class HeadlessAnalyzer {
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
for (Loaded<Program> loaded : loadResults) {
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
if (!loaded.getDomainObject().isTemporary()) {
|
Program program = loaded.getDomainObject(this);
|
||||||
try {
|
try {
|
||||||
DomainFile domainFile =
|
if (!program.isTemporary()) {
|
||||||
loaded.save(project, new MessageLog(), TaskMonitor.DUMMY);
|
try {
|
||||||
Msg.info(this, String.format("REPORT: Save succeeded for: %s (%s)", loaded,
|
DomainFile domainFile = loaded.save(TaskMonitor.DUMMY);
|
||||||
domainFile));
|
Msg.info(this, String.format("REPORT: Save succeeded for: %s (%s)",
|
||||||
}
|
loaded, domainFile));
|
||||||
catch (IOException e) {
|
}
|
||||||
Msg.info(this, "REPORT: Save failed for: " + loaded);
|
catch (IOException e) {
|
||||||
}
|
Msg.info(this, "REPORT: Save failed for: " + loaded);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (options.readOnly) {
|
|
||||||
Msg.info(this,
|
|
||||||
"REPORT: Discarded file import due to readOnly option: " + loaded);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Msg.info(this, "REPORT: Discarded file import as a result of script " +
|
if (options.readOnly) {
|
||||||
"activity or analysis timeout: " + loaded);
|
Msg.info(this,
|
||||||
|
"REPORT: Discarded file import due to readOnly option: " + loaded);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.info(this, "REPORT: Discarded file import as a result of script " +
|
||||||
|
"activity or analysis timeout: " + loaded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit changes
|
// Commit changes
|
||||||
if (options.commit) {
|
if (options.commit) {
|
||||||
for (Loaded<Program> loaded : loadResults) {
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
if (!loaded.getDomainObject().isTemporary()) {
|
Program program = loaded.getDomainObject(this);
|
||||||
if (loaded == primary) {
|
try {
|
||||||
AutoAnalysisManager.getAnalysisManager(primaryProgram).dispose();
|
if (!program.isTemporary()) {
|
||||||
|
if (loaded == primary) {
|
||||||
|
AutoAnalysisManager.getAnalysisManager(primaryProgram).dispose();
|
||||||
|
}
|
||||||
|
commitProgram(loaded.getSavedDomainFile());
|
||||||
}
|
}
|
||||||
loaded.release(this);
|
}
|
||||||
commitProgram(loaded.getSavedDomainFile());
|
finally {
|
||||||
|
program.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1621,7 +1644,7 @@ public class HeadlessAnalyzer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (LoadException e) {
|
catch (LoadException e) {
|
||||||
Msg.error(this, "The AutoImporter could not successfully load " + fsrl +
|
Msg.error(this, "The ProgramLoader could not successfully load " + fsrl +
|
||||||
" with the provided import parameters. Please ensure that any specified" +
|
" with the provided import parameters. Please ensure that any specified" +
|
||||||
" processor/cspec arguments are compatible with the loader that is used during" +
|
" processor/cspec arguments are compatible with the loader that is used during" +
|
||||||
" import and try again.");
|
" import and try again.");
|
||||||
|
@ -1638,37 +1661,12 @@ public class HeadlessAnalyzer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (loadResults != null) {
|
if (primaryProgram != null) {
|
||||||
loadResults.release(this);
|
primaryProgram.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadResults<Program> loadPrograms(FSRL fsrl, String folderPath)
|
|
||||||
throws VersionException, InvalidNameException, DuplicateNameException,
|
|
||||||
CancelledException, IOException, LoadException {
|
|
||||||
MessageLog messageLog = new MessageLog();
|
|
||||||
|
|
||||||
if (options.loaderClass == null) {
|
|
||||||
// User did not specify a loader
|
|
||||||
if (options.language == null) {
|
|
||||||
return AutoImporter.importByUsingBestGuess(fsrl, project, folderPath, this,
|
|
||||||
messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
return AutoImporter.importByLookingForLcs(fsrl, project, folderPath, options.language,
|
|
||||||
options.compilerSpec, this, messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// User specified a loader
|
|
||||||
if (options.language == null) {
|
|
||||||
return AutoImporter.importByUsingSpecificLoaderClass(fsrl, project, folderPath,
|
|
||||||
options.loaderClass, options.loaderArgs, this, messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
return AutoImporter.importByUsingSpecificLoaderClassAndLcs(fsrl, project, folderPath,
|
|
||||||
options.loaderClass, options.loaderArgs, options.language, options.compilerSpec, this,
|
|
||||||
messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processWithImport(FSRL fsrl, String folderPath, Integer depth, boolean isFirstTime)
|
private void processWithImport(FSRL fsrl, String folderPath, Integer depth, boolean isFirstTime)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (RefdFile refdFile = fsService.getRefdFile(fsrl, TaskMonitor.DUMMY)) {
|
try (RefdFile refdFile = fsService.getRefdFile(fsrl, TaskMonitor.DUMMY)) {
|
||||||
|
|
|
@ -490,30 +490,29 @@ public class HeadlessOptions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the loader to use for imports, as well as any loader-specific arguments. A null loader
|
* Sets the loader to use for imports, as well as any loader-specific arguments. A null loader
|
||||||
* will attempt "best-guess" if possible. Loader arguments are not supported if a "best-guess"
|
* name will attempt a "best-guess" if possible.
|
||||||
* is made.
|
|
||||||
*
|
*
|
||||||
* @param loaderName The name (simple class name) of the loader to use.
|
* @param loaderName The name (simple class name) of the loader to use (could be null)
|
||||||
* @param loaderArgs A list of loader-specific arguments. Could be null if there are none.
|
* @throws InvalidInputException if an invalid loader name was specified
|
||||||
* @throws InvalidInputException if an invalid loader name was specified, or if loader arguments
|
|
||||||
* were specified but a loader was not.
|
|
||||||
*/
|
*/
|
||||||
public void setLoader(String loaderName, List<Pair<String, String>> loaderArgs)
|
public void setLoader(String loaderName) throws InvalidInputException {
|
||||||
throws InvalidInputException {
|
|
||||||
if (loaderName != null) {
|
if (loaderName != null) {
|
||||||
this.loaderClass = LoaderService.getLoaderClassByName(loaderName);
|
this.loaderClass = LoaderService.getLoaderClassByName(loaderName);
|
||||||
if (this.loaderClass == null) {
|
if (this.loaderClass == null) {
|
||||||
throw new InvalidInputException("Invalid loader name specified: " + loaderName);
|
throw new InvalidInputException("Invalid loader name specified: " + loaderName);
|
||||||
}
|
}
|
||||||
this.loaderArgs = loaderArgs;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (loaderArgs != null && loaderArgs.size() > 0) {
|
|
||||||
throw new InvalidInputException(
|
|
||||||
"Loader arguments defined without a loader being specified.");
|
|
||||||
}
|
|
||||||
this.loaderClass = null;
|
this.loaderClass = null;
|
||||||
this.loaderArgs = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the loader arguments
|
||||||
|
*
|
||||||
|
* @param loaderArgs A list of loader-specific arguments. Could be null if there are none.
|
||||||
|
*/
|
||||||
|
public void setLoaderArgs(List<Pair<String, String>> loaderArgs) {
|
||||||
|
this.loaderArgs = loaderArgs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,28 +17,27 @@ package ghidra.app.util.importer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
import ghidra.app.util.Option;
|
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
import ghidra.formats.gfilesystem.FileSystemService;
|
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.lang.CompilerSpec;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods to do {@link Program} imports automatically (without requiring user interaction)
|
* Utility methods to do {@link Program} imports automatically (without requiring user interaction)
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public final class AutoImporter {
|
public final class AutoImporter {
|
||||||
private AutoImporter() {
|
private AutoImporter() {
|
||||||
// service class; cannot instantiate
|
// service class; cannot instantiate
|
||||||
|
@ -50,10 +49,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param file The {@link File} to import
|
* @param file The {@link File} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -64,10 +63,12 @@ public final class AutoImporter {
|
||||||
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
||||||
* should be queried for their true project folder paths using
|
* should be queried for their true project folder paths using
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -76,13 +77,20 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingBestGuess(File file, Project project,
|
public static LoadResults<Program> importByUsingBestGuess(File file, Project project,
|
||||||
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
||||||
VersionException, LoadException {
|
VersionException, LoadException {
|
||||||
return importByUsingBestGuess(fileToFsrl(file), project, projectFolderPath, consumer,
|
return ProgramLoader.builder()
|
||||||
messageLog, monitor);
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,10 +99,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param fsrl The {@link FSRL} to import
|
* @param fsrl The {@link FSRL} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -105,10 +113,12 @@ public final class AutoImporter {
|
||||||
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
||||||
* should be queried for their true project folder paths using
|
* should be queried for their true project folder paths using
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -117,14 +127,20 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingBestGuess(FSRL fsrl, Project project,
|
public static LoadResults<Program> importByUsingBestGuess(FSRL fsrl, Project project,
|
||||||
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
||||||
VersionException, LoadException {
|
VersionException, LoadException {
|
||||||
return importFresh(fsrl, project, projectFolderPath, consumer, messageLog, monitor,
|
return ProgramLoader.builder()
|
||||||
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
.source(fsrl)
|
||||||
OptionChooser.DEFAULT_OPTIONS);
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,10 +149,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param provider The bytes to import
|
* @param provider The bytes to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -147,10 +163,12 @@ public final class AutoImporter {
|
||||||
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
||||||
* should be queried for their true project folder paths using
|
* should be queried for their true project folder paths using
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -159,14 +177,20 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingBestGuess(ByteProvider provider,
|
public static LoadResults<Program> importByUsingBestGuess(ByteProvider provider,
|
||||||
Project project, String projectFolderPath, Object consumer, MessageLog messageLog,
|
Project project, String projectFolderPath, Object consumer, MessageLog messageLog,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
||||||
InvalidNameException, VersionException, LoadException {
|
InvalidNameException, VersionException, LoadException {
|
||||||
return importFresh(provider, project, projectFolderPath, consumer, messageLog, monitor,
|
return ProgramLoader.builder()
|
||||||
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
.source(provider)
|
||||||
OptionChooser.DEFAULT_OPTIONS);
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,10 +198,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param file The {@link File} to import
|
* @param file The {@link File} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -190,10 +214,12 @@ public final class AutoImporter {
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param loaderClass The {@link Loader} class to use
|
* @param loaderClass The {@link Loader} class to use
|
||||||
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -202,14 +228,23 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingSpecificLoaderClass(File file,
|
public static LoadResults<Program> importByUsingSpecificLoaderClass(File file,
|
||||||
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
||||||
List<Pair<String, String>> loaderArgs, Object consumer, MessageLog messageLog,
|
List<Pair<String, String>> loaderArgs, Object consumer, MessageLog messageLog,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
||||||
InvalidNameException, VersionException, LoadException {
|
InvalidNameException, VersionException, LoadException {
|
||||||
return importByUsingSpecificLoaderClass(fileToFsrl(file), project, projectFolderPath,
|
return ProgramLoader.builder()
|
||||||
loaderClass, loaderArgs, consumer, messageLog, monitor);
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.loaders(loaderClass)
|
||||||
|
.loaderArgs(loaderArgs)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -217,10 +252,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param fsrl The {@link FSRL} to import
|
* @param fsrl The {@link FSRL} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -233,10 +268,12 @@ public final class AutoImporter {
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param loaderClass The {@link Loader} class to use
|
* @param loaderClass The {@link Loader} class to use
|
||||||
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -245,16 +282,23 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingSpecificLoaderClass(FSRL fsrl, Project project,
|
public static LoadResults<Program> importByUsingSpecificLoaderClass(FSRL fsrl, Project project,
|
||||||
String projectFolderPath, Class<? extends Loader> loaderClass,
|
String projectFolderPath, Class<? extends Loader> loaderClass,
|
||||||
List<Pair<String, String>> loaderArgs, Object consumer, MessageLog messageLog,
|
List<Pair<String, String>> loaderArgs, Object consumer, MessageLog messageLog,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
||||||
InvalidNameException, VersionException, LoadException {
|
InvalidNameException, VersionException, LoadException {
|
||||||
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, loaderArgs);
|
return ProgramLoader.builder()
|
||||||
return importFresh(fsrl, project, projectFolderPath, consumer, messageLog, monitor,
|
.source(fsrl)
|
||||||
loaderFilter, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
.project(project)
|
||||||
new LoaderArgsOptionChooser(loaderFilter));
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.loaders(loaderClass)
|
||||||
|
.loaderArgs(loaderArgs)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,10 +307,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param file The {@link File} to import
|
* @param file The {@link File} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -279,10 +323,12 @@ public final class AutoImporter {
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param language The desired {@link Language}
|
* @param language The desired {@link Language}
|
||||||
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -291,13 +337,22 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByLookingForLcs(File file, Project project,
|
public static LoadResults<Program> importByLookingForLcs(File file, Project project,
|
||||||
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
||||||
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
||||||
return importByLookingForLcs(fileToFsrl(file), project, projectFolderPath, language,
|
return ProgramLoader.builder()
|
||||||
compilerSpec, consumer, messageLog, monitor);
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -306,10 +361,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param fsrl The {@link FSRL} to import
|
* @param fsrl The {@link FSRL} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -322,10 +377,12 @@ public final class AutoImporter {
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param language The desired {@link Language}
|
* @param language The desired {@link Language}
|
||||||
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -334,14 +391,22 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByLookingForLcs(FSRL fsrl, Project project,
|
public static LoadResults<Program> importByLookingForLcs(FSRL fsrl, Project project,
|
||||||
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
||||||
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
||||||
return importFresh(fsrl, project, projectFolderPath, consumer, messageLog, monitor,
|
return ProgramLoader.builder()
|
||||||
LoaderService.ACCEPT_ALL, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
.source(fsrl)
|
||||||
OptionChooser.DEFAULT_OPTIONS);
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -350,10 +415,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param file The {@link File} to import
|
* @param file The {@link File} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -368,10 +433,12 @@ public final class AutoImporter {
|
||||||
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
||||||
* @param language The desired {@link Language}
|
* @param language The desired {@link Language}
|
||||||
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -379,14 +446,25 @@ public final class AutoImporter {
|
||||||
* @throws InvalidNameException if an invalid {@link Program} name was used during load
|
* @throws InvalidNameException if an invalid {@link Program} name was used during load
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingSpecificLoaderClassAndLcs(File file,
|
public static LoadResults<Program> importByUsingSpecificLoaderClassAndLcs(File file,
|
||||||
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
||||||
List<Pair<String, String>> loaderArgs, Language language, CompilerSpec compilerSpec,
|
List<Pair<String, String>> loaderArgs, Language language, CompilerSpec compilerSpec,
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
||||||
return importByUsingSpecificLoaderClassAndLcs(fileToFsrl(file), project, projectFolderPath,
|
return ProgramLoader.builder()
|
||||||
loaderClass, loaderArgs, language, compilerSpec, consumer, messageLog, monitor);
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.loaders(loaderClass)
|
||||||
|
.loaderArgs(loaderArgs)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -395,10 +473,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param fsrl The {@link FSRL} to import
|
* @param fsrl The {@link FSRL} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -413,10 +491,12 @@ public final class AutoImporter {
|
||||||
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
||||||
* @param language The desired {@link Language}
|
* @param language The desired {@link Language}
|
||||||
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -424,31 +504,37 @@ public final class AutoImporter {
|
||||||
* @throws InvalidNameException if an invalid {@link Program} name was used during load
|
* @throws InvalidNameException if an invalid {@link Program} name was used during load
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importByUsingSpecificLoaderClassAndLcs(FSRL fsrl,
|
public static LoadResults<Program> importByUsingSpecificLoaderClassAndLcs(FSRL fsrl,
|
||||||
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
||||||
List<Pair<String, String>> loaderArgs, Language language, CompilerSpec compilerSpec,
|
List<Pair<String, String>> loaderArgs, Language language, CompilerSpec compilerSpec,
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
||||||
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, loaderArgs);
|
return ProgramLoader.builder()
|
||||||
return importFresh(fsrl, project, projectFolderPath, consumer, messageLog, monitor,
|
.source(fsrl)
|
||||||
loaderFilter, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
.project(project)
|
||||||
new LoaderArgsOptionChooser(loaderFilter));
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.loaders(loaderClass)
|
||||||
|
.loaderArgs(loaderArgs)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Predicate<Loader> BINARY_LOADER =
|
|
||||||
new SingleLoaderFilter(BinaryLoader.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically imports the given {@link File} with the {@link BinaryLoader}, using the given
|
* Automatically imports the given {@link File} with the {@link BinaryLoader}, using the given
|
||||||
* language and compiler specification.
|
* language and compiler specification.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program} is
|
* Note that when the import completes, the returned {@link Loaded} {@link Program} is
|
||||||
* not saved to a project. That is the responsibility of the caller (see
|
* not saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link Loaded#save(Project, MessageLog, TaskMonitor)}).
|
* {@link Loaded#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program} with {@link Loaded#release(Object)} when it is no longer needed.
|
* {@link Program} with {@link Loaded#close()} when it is no longer needed.
|
||||||
*
|
*
|
||||||
* @param file The {@link File} to import
|
* @param file The {@link File} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -461,7 +547,9 @@ public final class AutoImporter {
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param language The desired {@link Language}
|
* @param language The desired {@link Language}
|
||||||
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link Loaded}
|
||||||
|
* {@link Program}, used to ensure the underlying {@link Program} is only closed when every
|
||||||
|
* consumer is done with it (see {@link Loaded#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link Loaded} {@link Program} (created but not saved)
|
* @return The {@link Loaded} {@link Program} (created but not saved)
|
||||||
|
@ -472,15 +560,24 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static Loaded<Program> importAsBinary(File file, Project project,
|
public static Loaded<Program> importAsBinary(File file, Project project,
|
||||||
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
||||||
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
||||||
LoadResults<Program> loadResults = importFresh(file, project, projectFolderPath, consumer,
|
LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
messageLog, monitor, BINARY_LOADER, new LcsHintLoadSpecChooser(language, compilerSpec),
|
.source(file)
|
||||||
null, OptionChooser.DEFAULT_OPTIONS);
|
.project(project)
|
||||||
loadResults.releaseNonPrimary(consumer);
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.loaders(BinaryLoader.class)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
|
loadResults.getNonPrimary().forEach(Loaded::close);
|
||||||
return loadResults.getPrimary();
|
return loadResults.getPrimary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,10 +587,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program} is
|
* Note that when the import completes, the returned {@link Loaded} {@link Program} is
|
||||||
* not saved to a project. That is the responsibility of the caller (see
|
* not saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link Loaded#save(Project, MessageLog, TaskMonitor)}).
|
* {@link Loaded#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program} with {@link Loaded#release(Object)} when it is no longer needed.
|
* {@link Program} with {@link Loaded#close()} when it is no longer needed.
|
||||||
*
|
*
|
||||||
* @param bytes The bytes to import
|
* @param bytes The bytes to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -506,7 +603,9 @@ public final class AutoImporter {
|
||||||
* {@link Loaded#getProjectFolderPath()}.
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @param language The desired {@link Language}
|
* @param language The desired {@link Language}
|
||||||
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link Loaded}
|
||||||
|
* {@link Program}, used to ensure the underlying {@link Program} is only closed when every
|
||||||
|
* consumer is done with it (see {@link Loaded#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link Loaded} {@link Program} (created but not saved)
|
* @return The {@link Loaded} {@link Program} (created but not saved)
|
||||||
|
@ -517,16 +616,25 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static Loaded<Program> importAsBinary(ByteProvider bytes, Project project,
|
public static Loaded<Program> importAsBinary(ByteProvider bytes, Project project,
|
||||||
String projectFolderPath, Language language, CompilerSpec compilerSpec,
|
String projectFolderPath, Language language, CompilerSpec compilerSpec,
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
||||||
LoadException {
|
LoadException {
|
||||||
LoadResults<Program> loadResults = importFresh(bytes, project, projectFolderPath, consumer,
|
LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
messageLog, monitor, BINARY_LOADER, new LcsHintLoadSpecChooser(language, compilerSpec),
|
.source(bytes)
|
||||||
null, OptionChooser.DEFAULT_OPTIONS);
|
.project(project)
|
||||||
loadResults.releaseNonPrimary(consumer);
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.loaders(BinaryLoader.class)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
|
loadResults.getNonPrimary().forEach(Loaded::close);
|
||||||
return loadResults.getPrimary();
|
return loadResults.getPrimary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,10 +643,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param file The {@link File} to import
|
* @param file The {@link File} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -556,10 +664,12 @@ public final class AutoImporter {
|
||||||
* {@link Loader}'s preferred name.
|
* {@link Loader}'s preferred name.
|
||||||
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
||||||
* used
|
* used
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -568,15 +678,27 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importFresh(File file, Project project,
|
public static LoadResults<Program> importFresh(File file, Project project,
|
||||||
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
||||||
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
||||||
String importNameOverride, OptionChooser optionChooser) throws IOException,
|
String importNameOverride, OptionChooser optionChooser) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
||||||
LoadException {
|
LoadException {
|
||||||
return importFresh(fileToFsrl(file), project, projectFolderPath, consumer, messageLog,
|
return ProgramLoader.builder()
|
||||||
monitor, loaderFilter, loadSpecChooser, importNameOverride, optionChooser);
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.name(importNameOverride)
|
||||||
|
.loaders(loaderFilter)
|
||||||
|
.loaderArgs(optionChooser.getArgs())
|
||||||
|
.language(loadSpecChooser.getLanguageId())
|
||||||
|
.compiler(loadSpecChooser.getCompilerSpecId())
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -584,10 +706,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param fsrl The {@link FSRL} to import
|
* @param fsrl The {@link FSRL} to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -605,10 +727,12 @@ public final class AutoImporter {
|
||||||
* {@link Loader}'s preferred name.
|
* {@link Loader}'s preferred name.
|
||||||
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
||||||
* used
|
* used
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -617,22 +741,27 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importFresh(FSRL fsrl, Project project,
|
public static LoadResults<Program> importFresh(FSRL fsrl, Project project,
|
||||||
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
||||||
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
||||||
String importNameOverride, OptionChooser optionChooser)
|
String importNameOverride, OptionChooser optionChooser)
|
||||||
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
||||||
VersionException, LoadException {
|
VersionException, LoadException {
|
||||||
if (fsrl == null) {
|
return ProgramLoader.builder()
|
||||||
throw new LoadException("Cannot load null fsrl");
|
.source(fsrl)
|
||||||
}
|
.project(project)
|
||||||
|
.projectFolderPath(projectFolderPath)
|
||||||
try (ByteProvider provider =
|
.name(importNameOverride)
|
||||||
FileSystemService.getInstance().getByteProvider(fsrl, true, monitor)) {
|
.loaders(loaderFilter)
|
||||||
return importFresh(provider, project, projectFolderPath, consumer, messageLog, monitor,
|
.loaderArgs(optionChooser.getArgs())
|
||||||
loaderFilter, loadSpecChooser, importNameOverride, optionChooser);
|
.language(loadSpecChooser.getLanguageId())
|
||||||
}
|
.compiler(loadSpecChooser.getCompilerSpecId())
|
||||||
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -640,10 +769,10 @@ public final class AutoImporter {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
* {@link Program}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
*
|
*
|
||||||
* @param provider The bytes to import
|
* @param provider The bytes to import
|
||||||
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
@ -661,10 +790,12 @@ public final class AutoImporter {
|
||||||
* {@link Loader}'s preferred name.
|
* {@link Loader}'s preferred name.
|
||||||
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
||||||
* used
|
* used
|
||||||
* @param consumer A consumer
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @param monitor A task monitor
|
* @param monitor A task monitor
|
||||||
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
* @return The {@link LoadResults} which contains one or more {@link Loaded} {@link Program}s
|
||||||
* (created but not saved)
|
* (created but not saved)
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -673,103 +804,26 @@ public final class AutoImporter {
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
* failed language upgrade
|
* failed language upgrade
|
||||||
* @throws LoadException if nothing was loaded
|
* @throws LoadException if nothing was loaded
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public static LoadResults<Program> importFresh(ByteProvider provider, Project project,
|
public static LoadResults<Program> importFresh(ByteProvider provider, Project project,
|
||||||
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
||||||
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
||||||
String importNameOverride, OptionChooser optionChooser) throws IOException,
|
String importNameOverride, OptionChooser optionChooser) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
||||||
LoadException {
|
LoadException {
|
||||||
|
return ProgramLoader.builder()
|
||||||
if (provider == null) {
|
.source(provider)
|
||||||
throw new LoadException("Cannot load null provider");
|
.project(project)
|
||||||
}
|
.projectFolderPath(projectFolderPath)
|
||||||
|
.name(importNameOverride)
|
||||||
// Get the load spec
|
.loaders(loaderFilter)
|
||||||
LoadSpec loadSpec = getLoadSpec(loaderFilter, loadSpecChooser, provider);
|
.loaderArgs(optionChooser.getArgs())
|
||||||
if (loadSpec == null) {
|
.language(loadSpecChooser.getLanguageId())
|
||||||
throw new LoadException("No load spec found");
|
.compiler(loadSpecChooser.getCompilerSpecId())
|
||||||
}
|
.log(messageLog)
|
||||||
|
.monitor(monitor)
|
||||||
// Get the preferred import name
|
.load(consumer);
|
||||||
String importName = loadSpec.getLoader().getPreferredFileName(provider);
|
|
||||||
if (importNameOverride != null) {
|
|
||||||
importName = importNameOverride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect options
|
|
||||||
LanguageCompilerSpecPair languageCompilerSpecPair = loadSpec.getLanguageCompilerSpec();
|
|
||||||
AddressFactory addrFactory = null;// Address type options not permitted if null
|
|
||||||
if (languageCompilerSpecPair != null) {
|
|
||||||
// It is assumed that if languageCompilerSpecPair exists, then language will be found
|
|
||||||
addrFactory = DefaultLanguageService.getLanguageService()
|
|
||||||
.getLanguage(
|
|
||||||
languageCompilerSpecPair.languageID)
|
|
||||||
.getAddressFactory();
|
|
||||||
}
|
|
||||||
List<Option> loaderOptions = optionChooser.choose(
|
|
||||||
loadSpec.getLoader().getDefaultOptions(provider, loadSpec, null, false), addrFactory);
|
|
||||||
if (loaderOptions == null) {
|
|
||||||
throw new LoadException("Cannot load with null options");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import
|
|
||||||
Msg.info(AutoImporter.class, "Using Loader: " + loadSpec.getLoader().getName());
|
|
||||||
Msg.info(AutoImporter.class,
|
|
||||||
"Using Language/Compiler: " + loadSpec.getLanguageCompilerSpec());
|
|
||||||
Msg.info(AutoImporter.class, "Using Library Search Path: " +
|
|
||||||
Arrays.toString(LibrarySearchPathManager.getLibraryPaths()));
|
|
||||||
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
|
||||||
.load(provider, importName, project, projectFolderPath, loadSpec, loaderOptions,
|
|
||||||
messageLog, consumer, monitor);
|
|
||||||
|
|
||||||
// Optionally echo loader message log to application.log
|
|
||||||
if (!Loader.loggingDisabled && messageLog.hasMessages()) {
|
|
||||||
Msg.info(AutoImporter.class, "Additional info:\n" + messageLog.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter out and release non-Programs
|
|
||||||
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
|
||||||
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
|
||||||
if (loaded.getDomainObject() instanceof Program program) {
|
|
||||||
loadedPrograms.add(
|
|
||||||
new Loaded<Program>(program, loaded.getName(), loaded.getProjectFolderPath()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
loaded.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (loadedPrograms.isEmpty()) {
|
|
||||||
throw new LoadException("Domain objects were loaded, but none were Programs");
|
|
||||||
}
|
|
||||||
return new LoadResults<>(loadedPrograms);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LoadSpec getLoadSpec(Predicate<Loader> loaderFilter,
|
|
||||||
LoadSpecChooser loadSpecChooser, ByteProvider provider) {
|
|
||||||
LoaderMap loaderMap = LoaderService.getSupportedLoadSpecs(provider, loaderFilter);
|
|
||||||
|
|
||||||
LoadSpec loadSpec = loadSpecChooser.choose(loaderMap);
|
|
||||||
if (loadSpec != null) {
|
|
||||||
return loadSpec;
|
|
||||||
}
|
|
||||||
|
|
||||||
File f = provider.getFile();
|
|
||||||
String name = f != null ? f.getAbsolutePath() : provider.getName();
|
|
||||||
Msg.info(AutoImporter.class, "No load spec found for import file: " + name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a {@link File} to a local file system {@link FSRL}
|
|
||||||
* @param file The {@link File} to convert
|
|
||||||
* @return A {@link FSRL} that represents the given {@link File}
|
|
||||||
* @throws LoadException if the given {@link File} is null
|
|
||||||
*/
|
|
||||||
private static FSRL fileToFsrl(File file) throws LoadException {
|
|
||||||
if (file == null) {
|
|
||||||
throw new LoadException("Cannot load null file");
|
|
||||||
}
|
|
||||||
return FileSystemService.getInstance().getLocalFSRL(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,11 @@ public class CsHintLoadSpecChooser implements LoadSpecChooser {
|
||||||
this(new CompilerSpecID(compilerSpecID));
|
this(new CompilerSpecID(compilerSpecID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompilerSpecID getCompilerSpecId() {
|
||||||
|
return compilerSpecID;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoadSpec choose(LoaderMap loaderMap) {
|
public LoadSpec choose(LoaderMap loaderMap) {
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
package ghidra.app.util.importer;
|
package ghidra.app.util.importer;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import util.CollectionUtils;
|
import util.CollectionUtils;
|
||||||
|
|
||||||
|
@ -37,13 +39,46 @@ public class LcsHintLoadSpecChooser implements LoadSpecChooser {
|
||||||
* {@link CompilerSpec}.
|
* {@link CompilerSpec}.
|
||||||
*
|
*
|
||||||
* @param language The {@link Language} to use (should not be null)
|
* @param language The {@link Language} to use (should not be null)
|
||||||
* @param compilerSpec The {@link CompilerSpec} to use (f null default compiler spec will be used)
|
* @param compilerSpec The {@link CompilerSpec} to use (if null default compiler spec will be
|
||||||
|
* used)
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
*/
|
*/
|
||||||
public LcsHintLoadSpecChooser(Language language, CompilerSpec compilerSpec) {
|
public LcsHintLoadSpecChooser(Language language, CompilerSpec compilerSpec)
|
||||||
this.languageID = language.getLanguageID();
|
throws LanguageNotFoundException {
|
||||||
this.compilerSpecID =
|
this(language.getLanguageID(),
|
||||||
(compilerSpec == null) ? language.getDefaultCompilerSpec().getCompilerSpecID()
|
compilerSpec == null ? language.getDefaultCompilerSpec().getCompilerSpecID()
|
||||||
: compilerSpec.getCompilerSpecID();
|
: compilerSpec.getCompilerSpecID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link LcsHintLoadSpecChooser}.
|
||||||
|
* <p>
|
||||||
|
* NOTE: It is assumed that the given {@link LanguageID} is valid and it supports the given
|
||||||
|
* {@link CompilerSpecID}.
|
||||||
|
*
|
||||||
|
* @param languageId The {@link LanguageID} to use (should not be null)
|
||||||
|
* @param compilerSpecId The {@link CompilerSpecID} to use (if null default compiler spec will
|
||||||
|
* be used)
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
*/
|
||||||
|
public LcsHintLoadSpecChooser(LanguageID languageId, CompilerSpecID compilerSpecId)
|
||||||
|
throws LanguageNotFoundException {
|
||||||
|
this.languageID = languageId;
|
||||||
|
this.compilerSpecID = Objects.requireNonNullElse(compilerSpecId,
|
||||||
|
DefaultLanguageService.getLanguageService()
|
||||||
|
.getLanguage(languageID)
|
||||||
|
.getDefaultCompilerSpec()
|
||||||
|
.getCompilerSpecID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LanguageID getLanguageId() {
|
||||||
|
return languageID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompilerSpecID getCompilerSpecId() {
|
||||||
|
return compilerSpecID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
package ghidra.app.util.importer;
|
package ghidra.app.util.importer;
|
||||||
|
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chooses a {@link LoadSpec} for a {@link Loader} to use based on some criteria
|
* Chooses a {@link LoadSpec} for a {@link Loader} to use based on some criteria
|
||||||
|
@ -31,6 +33,26 @@ public interface LoadSpecChooser {
|
||||||
*/
|
*/
|
||||||
public LoadSpec choose(LoaderMap loaderMap);
|
public LoadSpec choose(LoaderMap loaderMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the desired {@link LanguageID} associated with this chooser
|
||||||
|
*
|
||||||
|
* @return the desired {@link LanguageID} associated with this chooser, or {@code null} to mean
|
||||||
|
* "any"
|
||||||
|
*/
|
||||||
|
public default LanguageID getLanguageId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the desired {@link CompilerSpecID} associated with this chooser
|
||||||
|
*
|
||||||
|
* @return the desired {@link CompilerSpecID} associated with this chooser, or {@code null} to
|
||||||
|
* mean "any"
|
||||||
|
*/
|
||||||
|
public default CompilerSpecID getCompilerSpecId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chooses the first "preferred" {@link LoadSpec}
|
* Chooses the first "preferred" {@link LoadSpec}
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,18 +19,29 @@ import java.util.List;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.opinion.Loader;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An option chooser that applies loader options that were passed in as command line arguments.
|
* An option chooser that applies loader options that were passed in as command line arguments.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaderArgs(List)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public class LoaderArgsOptionChooser implements OptionChooser {
|
public class LoaderArgsOptionChooser implements OptionChooser {
|
||||||
|
|
||||||
private List<Pair<String, String>> loaderArgs;
|
private List<Pair<String, String>> loaderArgs;
|
||||||
|
|
||||||
public LoaderArgsOptionChooser(SingleLoaderFilter loaderFilter) {
|
/**
|
||||||
this.loaderArgs = loaderFilter.getLoaderArgs();
|
* Creates a new {@link LoaderArgsOptionChooser}
|
||||||
|
*
|
||||||
|
* @param loaderArgs The {@link Loader} arguments
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaderArgs(List)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
|
public LoaderArgsOptionChooser(List<Pair<String, String>> loaderArgs) {
|
||||||
|
this.loaderArgs = loaderArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,12 +54,12 @@ public class LoaderArgsOptionChooser implements OptionChooser {
|
||||||
if (option.getArg() != null && arg.equalsIgnoreCase(option.getArg())) {
|
if (option.getArg() != null && arg.equalsIgnoreCase(option.getArg())) {
|
||||||
Object oldVal = option.getValue();
|
Object oldVal = option.getValue();
|
||||||
if (option.parseAndSetValueByType(val, addressFactory)) {
|
if (option.parseAndSetValueByType(val, addressFactory)) {
|
||||||
Msg.info(AutoImporter.class, String.format(
|
Msg.info(LoaderArgsOptionChooser.class, String.format(
|
||||||
"Successfully applied \"%s\" to \"%s\" (old: \"%s\", new: \"%s\")",
|
"Successfully applied \"%s\" to \"%s\" (old: \"%s\", new: \"%s\")",
|
||||||
arg, option.getName(), oldVal, val));
|
arg, option.getName(), oldVal, val));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Msg.error(AutoImporter.class, String.format(
|
Msg.error(LoaderArgsOptionChooser.class, String.format(
|
||||||
"Failed to apply \"%s\" to \"%s\" (old: \"%s\", bad: \"%s\")", arg,
|
"Failed to apply \"%s\" to \"%s\" (old: \"%s\", bad: \"%s\")", arg,
|
||||||
option.getName(), oldVal, val));
|
option.getName(), oldVal, val));
|
||||||
return null;
|
return null;
|
||||||
|
@ -58,11 +69,16 @@ public class LoaderArgsOptionChooser implements OptionChooser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!foundIt) {
|
if (!foundIt) {
|
||||||
Msg.error(AutoImporter.class, "Loader does not support " + arg + " argument");
|
Msg.warn(LoaderArgsOptionChooser.class,
|
||||||
return null;
|
"Skipping unsupported " + arg + " argument");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return optionChoices;
|
return optionChoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Pair<String, String>> getArgs() {
|
||||||
|
return loaderArgs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,41 @@ package ghidra.app.util.importer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import generic.stl.Pair;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.opinion.Loader;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooses which {@link Loader} options to use
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaderArgs(List)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface OptionChooser {
|
public interface OptionChooser {
|
||||||
public static final OptionChooser DEFAULT_OPTIONS = (choices, addressFactory) -> choices;
|
public static final OptionChooser DEFAULT_OPTIONS = (choices, addressFactory) -> choices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooses which {@link Loader} options to use
|
||||||
|
*
|
||||||
|
* @param optionChoices A {@link List} of available {@link Loader} options
|
||||||
|
* @param addressFactory The address factory
|
||||||
|
* @return The {@link List} of {@link Loader} options to use
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaderArgs(List)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
List<Option> choose(List<Option> optionChoices, AddressFactory addressFactory);
|
List<Option> choose(List<Option> optionChoices, AddressFactory addressFactory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Loader} arguments associated with this {@link OptionChooser}
|
||||||
|
*
|
||||||
|
* @return The {@link Loader} arguments associated with this {@link OptionChooser}
|
||||||
|
* @throws UnsupportedOperationException if a subclass has not implemented this method
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaderArgs(List)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
|
public default List<Pair<String, String>> getArgs() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,582 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.importer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import generic.stl.Pair;
|
||||||
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
|
import ghidra.app.util.opinion.*;
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.program.model.address.AddressFactory;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to load (import) a new {@link Program}
|
||||||
|
*/
|
||||||
|
public class ProgramLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a new {@link ProgramLoader} {@link Builder} which can be used to load a new
|
||||||
|
* {@link Program}
|
||||||
|
*
|
||||||
|
* @return A new {@link ProgramLoader} {@link Builder} which can be used to load a new
|
||||||
|
* {@link Program}
|
||||||
|
*/
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to configure and perform a {@link Program} load
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private ByteProvider provider;
|
||||||
|
private FSRL fsrl;
|
||||||
|
private File file;
|
||||||
|
private byte[] bytes;
|
||||||
|
private Project project;
|
||||||
|
private String projectFolderPath;
|
||||||
|
private String importNameOverride;
|
||||||
|
private Predicate<Loader> loaderFilter = LoaderService.ACCEPT_ALL;
|
||||||
|
private List<Pair<String, String>> loaderArgs = new ArrayList<>();
|
||||||
|
private LanguageID languageId;
|
||||||
|
private CompilerSpecID compilerSpecId;
|
||||||
|
private MessageLog log = new MessageLog();
|
||||||
|
private TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Builder}. Not intended to be used outside of {@link ProgramLoader}.
|
||||||
|
*/
|
||||||
|
private Builder() {
|
||||||
|
// Prevent public instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the required import source to the given {@link ByteProvider}.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Any previously defined sources will be overwritten.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Ownership of the given {@link ByteProvider} is not transfered to this
|
||||||
|
* {@link Builder}, so it is the responsibility of the caller to properly
|
||||||
|
* {@link ByteProvider#close() close} it when done.
|
||||||
|
*
|
||||||
|
* @param p The {@link ByteProvider} to import. A {@code null} value will unset the source.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder source(ByteProvider p) {
|
||||||
|
this.provider = p;
|
||||||
|
this.fsrl = null;
|
||||||
|
this.file = null;
|
||||||
|
this.bytes = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the required import source to the given {@link FSRL}
|
||||||
|
* <p>
|
||||||
|
* NOTE: Any previously defined sources will be overwritten
|
||||||
|
*
|
||||||
|
* @param f The {@link FSRL} to import. A {@code null} value will unset the source.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder source(FSRL f) {
|
||||||
|
this.provider = null;
|
||||||
|
this.fsrl = f;
|
||||||
|
this.file = null;
|
||||||
|
this.bytes = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the required import source to the given {@link File}
|
||||||
|
* <p>
|
||||||
|
* NOTE: Any previously defined sources will be overwritten
|
||||||
|
*
|
||||||
|
* @param f The {@link File} to import. A {@code null} value will unset the source.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder source(File f) {
|
||||||
|
this.provider = null;
|
||||||
|
this.fsrl = null;
|
||||||
|
this.file = f;
|
||||||
|
this.bytes = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the required import source to the given bytes
|
||||||
|
* <p>
|
||||||
|
* NOTE: Any previously defined sources will be overwritten
|
||||||
|
*
|
||||||
|
* @param b The bytes to import. A {@code null} value will unset the source.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder source(byte[] b) {
|
||||||
|
this.provider = null;
|
||||||
|
this.fsrl = null;
|
||||||
|
this.file = null;
|
||||||
|
this.bytes = b;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Project}. Loaders can use this to take advantage of existing
|
||||||
|
* {@link DomainFolder}s and {@link DomainFile}s to do custom behaviors such as loading
|
||||||
|
* libraries.
|
||||||
|
* <p>
|
||||||
|
* By default, no {@link Project} is associated with the {@link ProgramLoader}.
|
||||||
|
*
|
||||||
|
* @param p The {@link Project}. A {@code null} value will unset the project.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder project(Project p) {
|
||||||
|
this.project = p;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the suggested project folder path for the {@link Loaded} {@link Program}s. This is
|
||||||
|
* just a suggestion, and a {@link Loader} implementation reserves the right to change it
|
||||||
|
* for each {@link Loaded} result. The {@link Loaded} results should be queried for their
|
||||||
|
* true project folder paths using {@link Loaded#getProjectFolderPath()}.
|
||||||
|
* <p>
|
||||||
|
* The default project folder path is the root of the project ({@code "/"}).
|
||||||
|
*
|
||||||
|
* @param path The suggested project folder path. A {@code null} value will revert the path
|
||||||
|
* back to the default value of ({@code "/"}).
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder projectFolderPath(String path) {
|
||||||
|
this.projectFolderPath = path;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name to use for the imported {@link Program}.
|
||||||
|
* <p>
|
||||||
|
* The default is the {@link Loader}'s preferred name.
|
||||||
|
*
|
||||||
|
* @param name The name to use for the imported {@link Program}. A {@code null} value will
|
||||||
|
* revert the name to the {@link Loader}'s preferred name.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder name(String name) {
|
||||||
|
this.importNameOverride = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the acceptable {@link Loader}s to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, all {@link Loader}s are accepted ({@link LoaderService#ACCEPT_ALL}).
|
||||||
|
*
|
||||||
|
* @param filter A filter used to limit the {@link Loader}s used during import. A
|
||||||
|
* {@code null} value will revert back to the default ({@link LoaderService#ACCEPT_ALL}).
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder loaders(Predicate<Loader> filter) {
|
||||||
|
this.loaderFilter = filter != null ? filter : LoaderService.ACCEPT_ALL;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the acceptable {@link Loader} to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, all {@link Loader}s are accepted ({@link LoaderService#ACCEPT_ALL}).
|
||||||
|
*
|
||||||
|
* @param cls The class of the {@link Loader} to use during import. A {@code null} value
|
||||||
|
* will revert back to the default ({@link LoaderService#ACCEPT_ALL}).
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder loaders(Class<? extends Loader> cls) {
|
||||||
|
this.loaderFilter =
|
||||||
|
cls != null ? loader -> loader.getClass().equals(cls) : LoaderService.ACCEPT_ALL;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Loader}s to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, all {@link Loader}s are accepted ({@link LoaderService#ACCEPT_ALL}).
|
||||||
|
*
|
||||||
|
* @param cls A {@link List} of classes of {@link Loader}s to use during import. A
|
||||||
|
* {@code null} value will revert back to the default ({@link LoaderService#ACCEPT_ALL}).
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder loaders(List<Class<? extends Loader>> cls) {
|
||||||
|
this.loaderFilter =
|
||||||
|
cls != null ? loader -> cls.contains(loader.getClass()) : LoaderService.ACCEPT_ALL;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Loader} arguments to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, no {@link Loader} arguments are used.
|
||||||
|
*
|
||||||
|
* @param args A {@link List} of {@link Loader} argument name/value {@link Pair}s to use
|
||||||
|
* during import. A {@code null} value will result in no {@link Loader} arguments being
|
||||||
|
* used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder loaderArgs(List<Pair<String, String>> args) {
|
||||||
|
this.loaderArgs = args != null ? new ArrayList<>(args) : new ArrayList<>();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given {@link Loader} argument to use during import.
|
||||||
|
*
|
||||||
|
* @param name A single {@link Loader} argument name to use during import.
|
||||||
|
* @param value The value that corresponds to the argument {@code name}
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder addLoaderArg(String name, String value) {
|
||||||
|
this.loaderArgs.add(new Pair<String, String>(name, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the language to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, the first "preferred" language is used.
|
||||||
|
*
|
||||||
|
* @param id The language id to use during import. A {@code null} value will result in the
|
||||||
|
* first "preferred" language being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder language(String id) {
|
||||||
|
this.languageId = id != null ? new LanguageID(id) : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the language to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, the first "preferred" language is used.
|
||||||
|
*
|
||||||
|
* @param id The {@link LanguageID} to use during import. A {@code null} value will result
|
||||||
|
* in the first "preferred" language being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder language(LanguageID id) {
|
||||||
|
this.languageId = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the language to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, the first "preferred" language is used.
|
||||||
|
*
|
||||||
|
* @param language The {@link Language} to use during import. A {@code null} value will
|
||||||
|
* result in the first "preferred" language being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder language(Language language) {
|
||||||
|
this.languageId = language != null ? language.getLanguageID() : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the compiler to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, the processor's default compiler is used.
|
||||||
|
*
|
||||||
|
* @param id The compiler spec id to use during import. A {@code null} value will result in
|
||||||
|
* the language's default compiler being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder compiler(String id) {
|
||||||
|
this.compilerSpecId = id != null ? new CompilerSpecID(id) : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the compiler to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, the processor's default compiler is used.
|
||||||
|
*
|
||||||
|
* @param id The {@link CompilerSpecID} to use during import. A {@code null} value will
|
||||||
|
* result in the language's default compiler being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder compiler(CompilerSpecID id) {
|
||||||
|
this.compilerSpecId = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the compiler to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, the processor's default compiler is used.
|
||||||
|
*
|
||||||
|
* @param cspec The {@link CompilerSpec} to use during import. A {@code null} value will
|
||||||
|
* result in the language's default compiler being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder compiler(CompilerSpec cspec) {
|
||||||
|
this.compilerSpecId = cspec != null ? cspec.getCompilerSpecID() : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link MessageLog log} to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, no log is used.
|
||||||
|
*
|
||||||
|
* @param messageLog The {@link MessageLog log} to use during import. A {@code null} value
|
||||||
|
* will result in not logging.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder log(MessageLog messageLog) {
|
||||||
|
this.log = messageLog;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link TaskMonitor} to use during import.
|
||||||
|
* <p>
|
||||||
|
* By default, {@link TaskMonitor#DUMMY} is used.
|
||||||
|
*
|
||||||
|
* @param mon The {@link TaskMonitor} to use during import. A {@code null} value will result
|
||||||
|
* in {@link TaskMonitor#DUMMY} being used.
|
||||||
|
* @return This {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder monitor(TaskMonitor mon) {
|
||||||
|
this.monitor = mon;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the specified {@link #source(ByteProvider) source} with this {@link Builder}'s
|
||||||
|
* current configuration
|
||||||
|
*
|
||||||
|
* @return The {@link LoadResults} which contains one or more {@link Loaded}
|
||||||
|
* {@link Program}s (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
*/
|
||||||
|
public LoadResults<Program> load() throws IOException, LanguageNotFoundException,
|
||||||
|
CancelledException, VersionException, LoadException {
|
||||||
|
return load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the specified {@link #source(ByteProvider) source} with this {@link Builder}'s
|
||||||
|
* current configuration.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This method exists to maintain compatibility with the {@link AutoImporter} class,
|
||||||
|
* whose methods require consumer objects to be passed in. It should not be used by clients
|
||||||
|
* (use {@link #load()} instead, which uses a built-in consumer).
|
||||||
|
*
|
||||||
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults},
|
||||||
|
* used to ensure the underlying {@link Program}s are only closed when every consumer is
|
||||||
|
* done with it (see {@link LoadResults#close()}).
|
||||||
|
* @return The {@link LoadResults} which contains one or more {@link Loaded}
|
||||||
|
* {@link Program}s (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link #load()} instead
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
|
LoadResults<Program> load(Object consumer) throws IOException, LanguageNotFoundException,
|
||||||
|
CancelledException, VersionException, LoadException {
|
||||||
|
try (ByteProvider p = getSourceAsProvider()) {
|
||||||
|
|
||||||
|
LoadSpec loadSpec = getLoadSpec(p);
|
||||||
|
List<Option> loaderOptions = getLoaderOptions(p, loadSpec);
|
||||||
|
String importName = Objects.requireNonNullElse(importNameOverride,
|
||||||
|
loadSpec.getLoader().getPreferredFileName(p));
|
||||||
|
|
||||||
|
// Load
|
||||||
|
Msg.info(ProgramLoader.class, "Using Loader: " + loadSpec.getLoader().getName());
|
||||||
|
Msg.info(ProgramLoader.class,
|
||||||
|
"Using Language/Compiler: " + loadSpec.getLanguageCompilerSpec());
|
||||||
|
Msg.info(ProgramLoader.class, "Using Library Search Path: " +
|
||||||
|
Arrays.toString(LibrarySearchPathManager.getLibraryPaths()));
|
||||||
|
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
|
.load(p, importName, project, projectFolderPath, loadSpec, loaderOptions,
|
||||||
|
log, Objects.requireNonNullElse(consumer, this), monitor);
|
||||||
|
|
||||||
|
// Optionally echo loader message log to application.log
|
||||||
|
if (!Loader.loggingDisabled && log.hasMessages()) {
|
||||||
|
Msg.info(ProgramLoader.class, "Additional info:\n" + log);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out and release non-Programs
|
||||||
|
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
||||||
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
|
if (Program.class.isAssignableFrom(loaded.getDomainObjectType())) {
|
||||||
|
loadedPrograms.add((Loaded<Program>) loaded);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
loaded.close();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loadedPrograms.isEmpty()) {
|
||||||
|
throw new LoadException("Domain objects were loaded, but none were Programs");
|
||||||
|
}
|
||||||
|
return new LoadResults<>(loadedPrograms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this {@link Builder}'s source as a {@link ByteProvider}
|
||||||
|
* <p>
|
||||||
|
* NOTE: The returned {@link ByteProvider} should always be
|
||||||
|
* {@link ByteProvider#close() closed} by the caller. If this {@link Builder}'s source
|
||||||
|
* originated from a {@link ByteProvider}, the {@link ByteProvider#close()} will be a
|
||||||
|
* no-op.
|
||||||
|
*
|
||||||
|
* @return This {@link Builder}'s source as a {@link Byte Provider}
|
||||||
|
* @throws IOException if there was an IO-related problem
|
||||||
|
* @throws LoadException if there was no defined source
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
*/
|
||||||
|
private ByteProvider getSourceAsProvider()
|
||||||
|
throws IOException, LoadException, CancelledException {
|
||||||
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
|
ByteProvider p;
|
||||||
|
if (provider != null) {
|
||||||
|
p = new ByteProviderWrapper(provider, provider.getFSRL()); // wrap to prevent closing
|
||||||
|
}
|
||||||
|
else if (fsrl != null) {
|
||||||
|
p = fsService.getByteProvider(fsrl, true, monitor);
|
||||||
|
}
|
||||||
|
else if (file != null) {
|
||||||
|
p = fsService.getByteProvider(fsService.getLocalFSRL(file), true, monitor);
|
||||||
|
}
|
||||||
|
else if (bytes != null) {
|
||||||
|
p = new ByteArrayProvider(bytes);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new LoadException("No source to import!");
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link LoadSpec} from the given {@link ByteProvider}
|
||||||
|
*
|
||||||
|
* @param p The {@link ByteProvider}
|
||||||
|
* @return A {@link LoadSpec}
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws LoadException if a {@link LoadSpec} was not found
|
||||||
|
*/
|
||||||
|
private LoadSpec getLoadSpec(ByteProvider p)
|
||||||
|
throws LanguageNotFoundException, LoadException {
|
||||||
|
LoaderMap loaderMap = LoaderService.getSupportedLoadSpecs(p, loaderFilter);
|
||||||
|
LoadSpecChooser loadSpecChooser =
|
||||||
|
languageId != null ? new LcsHintLoadSpecChooser(languageId, compilerSpecId)
|
||||||
|
: (compilerSpecId != null ? new CsHintLoadSpecChooser(compilerSpecId)
|
||||||
|
: LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED);
|
||||||
|
LoadSpec loadSpec = loadSpecChooser.choose(loaderMap);
|
||||||
|
if (loadSpec == null) {
|
||||||
|
String name = Objects.requireNonNullElse(p.getName(), "???");
|
||||||
|
Msg.info(ProgramLoader.class, "No load spec found for import file: " + name);
|
||||||
|
throw new LoadException("No load spec found");
|
||||||
|
}
|
||||||
|
return loadSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Loader} {@link Option}s, with any loader arguments applied
|
||||||
|
*
|
||||||
|
* @param p The {@link ByteProvider}
|
||||||
|
* @param loadSpec The {@link LoadSpec}
|
||||||
|
* @return The {@link Loader} {@link Option}s, with any loader arguments applied
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws LoadException if the {@link Loader} had {@code null} options
|
||||||
|
*/
|
||||||
|
private List<Option> getLoaderOptions(ByteProvider p, LoadSpec loadSpec)
|
||||||
|
throws LanguageNotFoundException, LoadException {
|
||||||
|
List<Option> options = loadSpec.getLoader().getDefaultOptions(p, loadSpec, null, false);
|
||||||
|
if (options == null) {
|
||||||
|
throw new LoadException("Cannot load with null options");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaderArgs == null) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
LanguageCompilerSpecPair languageCompilerSpecPair =
|
||||||
|
loadSpec.getLanguageCompilerSpec();
|
||||||
|
AddressFactory addrFactory = null; // Address type options not permitted if null
|
||||||
|
if (languageCompilerSpecPair != null) {
|
||||||
|
// It is assumed that if languageCompilerSpecPair exists, then language will be found
|
||||||
|
addrFactory = DefaultLanguageService.getLanguageService()
|
||||||
|
.getLanguage(languageCompilerSpecPair.languageID)
|
||||||
|
.getAddressFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Pair<String, String> pair : loaderArgs) {
|
||||||
|
String arg = pair.first, val = pair.second;
|
||||||
|
boolean foundIt = false;
|
||||||
|
for (Option option : options) {
|
||||||
|
if (option.getArg() != null && arg.equalsIgnoreCase(option.getArg())) {
|
||||||
|
Object oldVal = option.getValue();
|
||||||
|
if (option.parseAndSetValueByType(val, addrFactory)) {
|
||||||
|
Msg.info(ProgramLoader.class, String.format(
|
||||||
|
"Successfully applied \"%s\" to \"%s\" (old: \"%s\", new: \"%s\")",
|
||||||
|
arg, option.getName(), oldVal, val));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.error(ProgramLoader.class, String.format(
|
||||||
|
"Failed to apply \"%s\" to \"%s\" (old: \"%s\", bad: \"%s\")", arg,
|
||||||
|
option.getName(), oldVal, val));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
foundIt = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundIt) {
|
||||||
|
Msg.warn(ProgramLoader.class, "Skipping unsupported " + arg + " argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,54 +15,32 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.importer;
|
package ghidra.app.util.importer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.Loader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters on one specific loader
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaders(Class)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public class SingleLoaderFilter implements Predicate<Loader> {
|
public class SingleLoaderFilter implements Predicate<Loader> {
|
||||||
private final Class<? extends Loader> single;
|
private final Class<? extends Loader> single;
|
||||||
private List<Pair<String, String>> loaderArgs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new single loader filter from the given loader class.
|
* Create a new single loader filter from the given loader class.
|
||||||
*
|
*
|
||||||
* @param single The loader class used for this filter.
|
* @param single The loader class used for this filter.
|
||||||
|
* @deprecated Use {@link ProgramLoader.Builder#loaders(Class)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public SingleLoaderFilter(Class<? extends Loader> single) {
|
public SingleLoaderFilter(Class<? extends Loader> single) {
|
||||||
this.single = single;
|
this.single = single;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new single loader filter from the given loader class and loader command line
|
|
||||||
* argument list.
|
|
||||||
*
|
|
||||||
* @param single The loader class used for this filter.
|
|
||||||
* @param loaderArgs The loader arguments used for this filter. Could be null if there
|
|
||||||
* are not arguments.
|
|
||||||
*/
|
|
||||||
public SingleLoaderFilter(Class<? extends Loader> single,
|
|
||||||
List<Pair<String, String>> loaderArgs) {
|
|
||||||
this.single = single;
|
|
||||||
this.loaderArgs = loaderArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the loader arguments tied to the loader in this filter.
|
|
||||||
*
|
|
||||||
* @return The loader arguments tied to the loader in this filter. Could be null if there
|
|
||||||
* are no arguments.
|
|
||||||
*/
|
|
||||||
public List<Pair<String, String>> getLoaderArgs() {
|
|
||||||
return loaderArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(Loader loader) {
|
public boolean test(Loader loader) {
|
||||||
if (loader.getClass().equals(single)) {
|
return loader.getClass().equals(single);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.*;
|
import ghidra.app.util.importer.*;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.plugin.importer.ImporterPlugin;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Library;
|
import ghidra.program.model.listing.Library;
|
||||||
|
@ -49,23 +50,51 @@ import ghidra.util.task.TaskMonitor;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader {
|
public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option to attempt to fix up the {@link Loaded} {@link Program}'s external programs with
|
||||||
|
* libraries discovered in the project. This alone does not cause new library programs to be
|
||||||
|
* loaded.
|
||||||
|
*/
|
||||||
public static final String LINK_EXISTING_OPTION_NAME = "Link Existing Project Libraries";
|
public static final String LINK_EXISTING_OPTION_NAME = "Link Existing Project Libraries";
|
||||||
static final boolean LINK_EXISTING_OPTION_DEFAULT = true;
|
static final boolean LINK_EXISTING_OPTION_DEFAULT = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of a {@link DomainFolder} to search for libraries
|
||||||
|
*/
|
||||||
public static final String LINK_SEARCH_FOLDER_OPTION_NAME = "Project Library Search Folder";
|
public static final String LINK_SEARCH_FOLDER_OPTION_NAME = "Project Library Search Folder";
|
||||||
static final String LINK_SEARCH_FOLDER_OPTION_DEFAULT = "";
|
static final String LINK_SEARCH_FOLDER_OPTION_DEFAULT = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to search for libraries on disk (or in a {@link GFileSystem})
|
||||||
|
*/
|
||||||
public static final String LOAD_LIBRARY_OPTION_NAME = "Load Libraries From Disk";
|
public static final String LOAD_LIBRARY_OPTION_NAME = "Load Libraries From Disk";
|
||||||
static final boolean LOAD_LIBRARY_OPTION_DEFAULT = false;
|
static final boolean LOAD_LIBRARY_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dummy option used to produce a custom renderer to select library search paths.
|
||||||
|
*
|
||||||
|
* @see LibrarySearchPathDummyOption
|
||||||
|
*/
|
||||||
public static final String LIBRARY_SEARCH_PATH_DUMMY_OPTION_NAME = "Library Search Paths";
|
public static final String LIBRARY_SEARCH_PATH_DUMMY_OPTION_NAME = "Library Search Paths";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many levels of libraries to load
|
||||||
|
*/
|
||||||
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of a {@link DomainFolder} to save libraries to. This location will also be used as
|
||||||
|
* a location to {@link #LINK_EXISTING_OPTION_NAME search for already-loaded libraries}.
|
||||||
|
*/
|
||||||
public static final String LIBRARY_DEST_FOLDER_OPTION_NAME = "Library Destination Folder";
|
public static final String LIBRARY_DEST_FOLDER_OPTION_NAME = "Library Destination Folder";
|
||||||
static final String LIBRARY_DEST_FOLDER_OPTION_DEFAULT = "";
|
static final String LIBRARY_DEST_FOLDER_OPTION_DEFAULT = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hidden option used by the {@link ImporterPlugin}'s "Load Libraries" action to inform this
|
||||||
|
* {@link Loader} that the {@link Program} to import has already been saved to the project and
|
||||||
|
* is currently open, and that only libraries should be loaded.
|
||||||
|
*/
|
||||||
public static final String LOAD_ONLY_LIBRARIES_OPTION_NAME = "Only Load Libraries"; // hidden
|
public static final String LOAD_ONLY_LIBRARIES_OPTION_NAME = "Only Load Libraries"; // hidden
|
||||||
static final boolean LOAD_ONLY_LIBRARIES_OPTION_DEFAULT = false;
|
static final boolean LOAD_ONLY_LIBRARIES_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
|
@ -85,6 +114,31 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
Program program, TaskMonitor monitor, MessageLog log)
|
Program program, TaskMonitor monitor, MessageLog log)
|
||||||
throws CancelledException, IOException;
|
throws CancelledException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p>
|
||||||
|
* In addition to loading the given program bytes, this implementation will attempt to locate
|
||||||
|
* the libraries that the program links to from the
|
||||||
|
* {@link #LINK_SEARCH_FOLDER_OPTION_NAME project library search folder}, the
|
||||||
|
* {@link #LIBRARY_DEST_FOLDER_OPTION_NAME library destination folder} and the
|
||||||
|
* {@link #LOAD_LIBRARY_OPTION_NAME libraries found on disk}. All of these locations are
|
||||||
|
* controlled by loader options.
|
||||||
|
* <P>
|
||||||
|
* If the hidden {@link #LOAD_ONLY_LIBRARIES_OPTION_NAME} option is set and the given project
|
||||||
|
* is not {@code null}, it is assumed that a {@link DomainFile} exists at
|
||||||
|
* {@code projectFolderPath/loadedName}, is open, and the provider corresponds to its contents.
|
||||||
|
* If this is the case, the primary (first) {@link Loaded} {@link Program} in the returned list
|
||||||
|
* will NOT be affected by a {@link LoadResults#save(TaskMonitor)} operation. It will be the
|
||||||
|
* responsibility of the user to save this open program if desired.
|
||||||
|
*
|
||||||
|
* @return A {@link List} of one or more {@link Loaded} {@link Program}s (created but not
|
||||||
|
* saved). The first element in the {@link List} will the primary program, with the remaining
|
||||||
|
* elements being any newly loaded libraries.
|
||||||
|
* @throws LoadException if the load failed in an unexpected way. If the
|
||||||
|
* {@link #LOAD_ONLY_LIBRARIES_OPTION_NAME} option is set, this exception will be thrown if
|
||||||
|
* the {@link DomainFile} at {@code projectFolderPath/loadedName} does not correspond to an
|
||||||
|
* open {@link Program}.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String loadedName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String loadedName,
|
||||||
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
|
@ -101,21 +155,28 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (!shouldLoadOnlyLibraries(options)) {
|
if (!shouldLoadOnlyLibraries(options)) {
|
||||||
program = doLoad(provider, loadedName, loadSpec, libraryNameList, options, consumer,
|
program = doLoad(provider, loadedName, loadSpec, libraryNameList, options, consumer,
|
||||||
log, monitor);
|
log, monitor);
|
||||||
loadedProgramList.add(new Loaded<>(program, loadedName, projectFolderPath));
|
loadedProgramList.add(
|
||||||
|
new Loaded<>(program, loadedName, project, projectFolderPath, consumer));
|
||||||
log.appendMsg("------------------------------------------------\n");
|
log.appendMsg("------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
else if (project != null) {
|
else {
|
||||||
ProjectData projectData = project.getProjectData();
|
if (project == null) {
|
||||||
DomainFile domainFile = projectData.getFile(projectFolderPath + "/" + loadedName);
|
throw new LoadException("Cannot load only libraries...project is null");
|
||||||
|
}
|
||||||
|
DomainFile domainFile =
|
||||||
|
project.getProjectData().getFile(projectFolderPath + "/" + loadedName);
|
||||||
if (domainFile == null) {
|
if (domainFile == null) {
|
||||||
throw new LoadException(
|
throw new LoadException(
|
||||||
"Cannot load only libraries for a non-existant program");
|
"Cannot load only libraries for a non-existant program");
|
||||||
}
|
}
|
||||||
|
if (!Program.class.isAssignableFrom(domainFile.getDomainObjectClass())) {
|
||||||
|
throw new LoadException("Cannot load only libraries for a non-program");
|
||||||
|
}
|
||||||
program = (Program) domainFile.getOpenedDomainObject(consumer);
|
program = (Program) domainFile.getOpenedDomainObject(consumer);
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
throw new LoadException("Failed to acquire a Program");
|
throw new LoadException("Failed to acquire an open Program");
|
||||||
}
|
}
|
||||||
loadedProgramList.add(new Loaded<>(program, domainFile));
|
loadedProgramList.add(new LoadedOpen<>(program, domainFile, consumer));
|
||||||
libraryNameList.addAll(getLibraryNames(provider, program));
|
libraryNameList.addAll(getLibraryNames(provider, program));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +189,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedProgramList, consumer);
|
loadedProgramList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,32 +230,45 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
List<DomainFolder> searchFolders =
|
List<DomainFolder> searchFolders =
|
||||||
getLibrarySearchFolders(loadedPrograms, project, options, log);
|
getLibrarySearchFolders(loadedPrograms, project, options, log);
|
||||||
|
|
||||||
List<LibrarySearchPath> searchPaths = getLibrarySearchPaths(
|
Program firstProgram = loadedPrograms.getFirst().getDomainObject(this);
|
||||||
loadedPrograms.getFirst().getDomainObject(), loadSpec, options, log, monitor);
|
List<LibrarySearchPath> searchPaths;
|
||||||
|
try {
|
||||||
|
searchPaths = getLibrarySearchPaths(firstProgram, loadSpec, options, log, monitor);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
firstProgram.release(this);
|
||||||
|
}
|
||||||
|
|
||||||
List<Loaded<Program>> saveablePrograms =
|
List<Loaded<Program>> saveablePrograms = loadedPrograms
|
||||||
loadedPrograms.stream().filter(Predicate.not(Loaded::shouldDiscard)).toList();
|
.stream()
|
||||||
|
.filter(loaded -> loaded.check(Predicate.not(Program::isTemporary)))
|
||||||
|
.toList();
|
||||||
|
|
||||||
monitor.initialize(saveablePrograms.size());
|
monitor.initialize(saveablePrograms.size());
|
||||||
for (Loaded<Program> loadedProgram : saveablePrograms) {
|
for (Loaded<Program> loadedProgram : saveablePrograms) {
|
||||||
monitor.increment();
|
monitor.increment();
|
||||||
|
|
||||||
Program program = loadedProgram.getDomainObject();
|
Program program = loadedProgram.getDomainObject(this);
|
||||||
ExternalManager extManager = program.getExternalManager();
|
|
||||||
String[] extLibNames = extManager.getExternalLibraryNames();
|
|
||||||
if (extLibNames.length == 0 ||
|
|
||||||
(extLibNames.length == 1 && Library.UNKNOWN.equals(extLibNames[0]))) {
|
|
||||||
continue; // skip program if no libraries defined
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor.setMessage("Resolving..." + program.getName());
|
|
||||||
int id = program.startTransaction("Resolving external references");
|
|
||||||
try {
|
try {
|
||||||
resolveExternalLibraries(program, saveablePrograms, searchFolders, searchPaths,
|
ExternalManager extManager = program.getExternalManager();
|
||||||
options, monitor, log);
|
String[] extLibNames = extManager.getExternalLibraryNames();
|
||||||
|
if (extLibNames.length == 0 ||
|
||||||
|
(extLibNames.length == 1 && Library.UNKNOWN.equals(extLibNames[0]))) {
|
||||||
|
continue; // skip program if no libraries defined
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.setMessage("Resolving..." + program.getName());
|
||||||
|
int id = program.startTransaction("Resolving external references");
|
||||||
|
try {
|
||||||
|
resolveExternalLibraries(program, saveablePrograms, searchFolders, searchPaths,
|
||||||
|
options, monitor, log);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(id, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true);
|
program.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,16 +482,22 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*/
|
*/
|
||||||
protected List<DomainFolder> getLibrarySearchFolders(List<Loaded<Program>> loadedPrograms,
|
protected List<DomainFolder> getLibrarySearchFolders(List<Loaded<Program>> loadedPrograms,
|
||||||
Project project, List<Option> options, MessageLog log) {
|
Project project, List<Option> options, MessageLog log) {
|
||||||
List<DomainFolder> searchFolders = new ArrayList<>();
|
Program firstProgram = loadedPrograms.getFirst().getDomainObject(this);
|
||||||
String projectFolderPath = loadedPrograms.get(0).getProjectFolderPath();
|
try {
|
||||||
String destPath = getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
List<DomainFolder> searchFolders = new ArrayList<>();
|
||||||
DomainFolder destSearchFolder =
|
String projectFolderPath = loadedPrograms.get(0).getProjectFolderPath();
|
||||||
getLibraryDestinationSearchFolder(project, destPath, options);
|
String destPath = getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
||||||
DomainFolder linkSearchFolder = getLinkSearchFolder(project,
|
DomainFolder destSearchFolder =
|
||||||
loadedPrograms.getFirst().getDomainObject(), projectFolderPath, options, log);
|
getLibraryDestinationSearchFolder(project, destPath, options);
|
||||||
Optional.ofNullable(destSearchFolder).ifPresent(searchFolders::add);
|
DomainFolder linkSearchFolder =
|
||||||
Optional.ofNullable(linkSearchFolder).ifPresent(searchFolders::add);
|
getLinkSearchFolder(project, firstProgram, projectFolderPath, options, log);
|
||||||
return searchFolders;
|
Optional.ofNullable(destSearchFolder).ifPresent(searchFolders::add);
|
||||||
|
Optional.ofNullable(linkSearchFolder).ifPresent(searchFolders::add);
|
||||||
|
return searchFolders;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
firstProgram.release(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -559,18 +639,18 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
else if (isLoadLibraries(options) || shouldSearchAllPaths(program, options, log)) {
|
else if (isLoadLibraries(options) || shouldSearchAllPaths(program, options, log)) {
|
||||||
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(library, provider,
|
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(library, provider,
|
||||||
customSearchPaths, libraryDestFolderPath, unprocessed, depth,
|
project, customSearchPaths, libraryDestFolderPath, unprocessed, depth,
|
||||||
desiredLoadSpec, options, log, consumer, monitor);
|
desiredLoadSpec, options, log, consumer, monitor);
|
||||||
if (loadedLibrary == null) {
|
if (loadedLibrary == null) {
|
||||||
loadedLibrary = loadLibraryFromSearchPaths(library, provider, searchPaths,
|
loadedLibrary = loadLibraryFromSearchPaths(library, provider, project,
|
||||||
libraryDestFolderPath, unprocessed, depth, desiredLoadSpec, options,
|
searchPaths, libraryDestFolderPath, unprocessed, depth, desiredLoadSpec,
|
||||||
log, consumer, monitor);
|
options, log, consumer, monitor);
|
||||||
}
|
}
|
||||||
if (loadedLibrary != null) {
|
if (loadedLibrary != null) {
|
||||||
boolean discarding = !loadLibraries || unprocessedLibrary.discard();
|
boolean temporary = !loadLibraries || unprocessedLibrary.temporary();
|
||||||
loadedLibrary.setDiscard(discarding);
|
loadedLibrary.apply(p -> p.setTemporary(temporary));
|
||||||
loadedPrograms.add(loadedLibrary);
|
loadedPrograms.add(loadedLibrary);
|
||||||
log.appendMsg(discarding ? "Library not saved to project."
|
log.appendMsg(temporary ? "Library not saved to project."
|
||||||
: "Saving library to: " + loadedLibrary);
|
: "Saving library to: " + loadedLibrary);
|
||||||
}
|
}
|
||||||
log.appendMsg("------------------------------------------------\n");
|
log.appendMsg("------------------------------------------------\n");
|
||||||
|
@ -581,7 +661,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedPrograms, consumer);
|
loadedPrograms.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
Stream.of(customSearchPaths, searchPaths)
|
Stream.of(customSearchPaths, searchPaths)
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
|
@ -600,6 +680,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*
|
*
|
||||||
* @param library The library to load
|
* @param library The library to load
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
* @param provider The {@link ByteProvider} of the program being loaded
|
||||||
|
* @param project The {@link Project}. Could be null if there is no project.
|
||||||
* @param searchPaths A {@link List} of {@link LibrarySearchPath}s that will be searched
|
* @param searchPaths A {@link List} of {@link LibrarySearchPath}s that will be searched
|
||||||
* @param libraryDestFolderPath The path of the project folder to load the libraries into.
|
* @param libraryDestFolderPath The path of the project folder to load the libraries into.
|
||||||
* Could be null if the specified project is null or a destination folder path could not be
|
* Could be null if the specified project is null or a destination folder path could not be
|
||||||
|
@ -617,7 +698,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private Loaded<Program> loadLibraryFromSearchPaths(String library, ByteProvider provider,
|
private Loaded<Program> loadLibraryFromSearchPaths(String library, ByteProvider provider,
|
||||||
List<LibrarySearchPath> searchPaths, String libraryDestFolderPath,
|
Project project, List<LibrarySearchPath> searchPaths, String libraryDestFolderPath,
|
||||||
Queue<UnprocessedLibrary> unprocessed, int depth, LoadSpec desiredLoadSpec,
|
Queue<UnprocessedLibrary> unprocessed, int depth, LoadSpec desiredLoadSpec,
|
||||||
List<Option> options, MessageLog log, Object consumer, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
@ -662,7 +743,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
folderPath = joinPaths(folderPath, FilenameUtils.getFullPath(library));
|
folderPath = joinPaths(folderPath, FilenameUtils.getFullPath(library));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Loaded<Program>(libraryProgram, simpleLibraryName, folderPath);
|
return new Loaded<Program>(libraryProgram, simpleLibraryName, project, folderPath,
|
||||||
|
consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -947,9 +1029,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param name The name of the library
|
* @param name The name of the library
|
||||||
* @param depth The recursive load depth of the library (based on the original binary being
|
* @param depth The recursive load depth of the library (based on the original binary being
|
||||||
* loaded)
|
* loaded)
|
||||||
* @param discard True if the library should be discarded (not saved) after processing
|
* @param temporary True if the library is temporary and should be discarded prior to returning
|
||||||
|
* from the load
|
||||||
*/
|
*/
|
||||||
protected record UnprocessedLibrary(String name, int depth, boolean discard) {/**/}
|
protected record UnprocessedLibrary(String name, int depth, boolean temporary) {/**/}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link Queue} of {@link UnprocessedLibrary}s, initialized filled with the
|
* Creates a new {@link Queue} of {@link UnprocessedLibrary}s, initialized filled with the
|
||||||
|
|
|
@ -131,12 +131,14 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
if (shouldPerformOrdinalLookup(options)) {
|
if (shouldPerformOrdinalLookup(options)) {
|
||||||
List<Loaded<Program>> saveablePrograms =
|
List<Loaded<Program>> saveablePrograms = loadedPrograms
|
||||||
loadedPrograms.stream().filter(Predicate.not(Loaded::shouldDiscard)).toList();
|
.stream()
|
||||||
|
.filter(loaded -> loaded.check(Predicate.not(Program::isTemporary)))
|
||||||
|
.toList();
|
||||||
monitor.initialize(saveablePrograms.size());
|
monitor.initialize(saveablePrograms.size());
|
||||||
for (Loaded<Program> loadedProgram : saveablePrograms) {
|
for (Loaded<Program> loadedProgram : saveablePrograms) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
Program program = loadedProgram.getDomainObject();
|
Program program = loadedProgram.getDomainObject(this);
|
||||||
int id = program.startTransaction("Ordinal fixups");
|
int id = program.startTransaction("Ordinal fixups");
|
||||||
try {
|
try {
|
||||||
applyLibrarySymbols(program, messageLog, monitor);
|
applyLibrarySymbols(program, messageLog, monitor);
|
||||||
|
@ -144,6 +146,7 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true); // More efficient to commit when program will be discarded
|
program.endTransaction(id, true); // More efficient to commit when program will be discarded
|
||||||
|
program.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,11 +63,10 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the load completes, the returned {@link Loaded} {@link Program}s are not
|
* Note that when the load completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link Loaded#save(Project, MessageLog, TaskMonitor)}).
|
* {@link Loaded#save(TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link Program}s with {@link Loaded#release(Object)} when they are no longer
|
* {@link Program}s with {@link Loaded#close()} when they are no longer needed.
|
||||||
* needed.
|
|
||||||
*
|
*
|
||||||
* @param provider The bytes to load.
|
* @param provider The bytes to load.
|
||||||
* @param loadedName A suggested name for the primary {@link Loaded} {@link Program}.
|
* @param loadedName A suggested name for the primary {@link Loaded} {@link Program}.
|
||||||
|
@ -135,21 +134,26 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
try {
|
try {
|
||||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
Program program = loadedProgram.getDomainObject();
|
Program program = loadedProgram.getDomainObject(this);
|
||||||
applyProcessorLabels(options, program);
|
try {
|
||||||
program.setEventsEnabled(true);
|
applyProcessorLabels(options, program);
|
||||||
|
program.setEventsEnabled(true);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subclasses can perform custom post-load fix-ups
|
// Subclasses can perform custom post-load fix-ups
|
||||||
postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog, monitor);
|
postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog, monitor);
|
||||||
|
|
||||||
// Discard unneeded programs
|
// Discard temporary programs
|
||||||
Iterator<Loaded<Program>> iter = loadedPrograms.iterator();
|
Iterator<Loaded<Program>> iter = loadedPrograms.iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
Loaded<Program> loaded = iter.next();
|
Loaded<Program> loaded = iter.next();
|
||||||
if (loaded.shouldDiscard()) {
|
if (loaded.check(p -> p.isTemporary())) {
|
||||||
iter.remove();
|
iter.remove();
|
||||||
loaded.release(consumer);
|
loaded.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +162,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedPrograms, consumer);
|
loadedPrograms.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
postLoadCleanup(success);
|
postLoadCleanup(success);
|
||||||
}
|
}
|
||||||
|
@ -463,18 +467,6 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
return DefaultLanguageService.getLanguageService();
|
return DefaultLanguageService.getLanguageService();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the given consumer from each of the provided {@link Loaded loaded programs}
|
|
||||||
*
|
|
||||||
* @param loadedPrograms A list of {@link Loaded loaded programs} which are no longer being used
|
|
||||||
* @param consumer The consumer that was marking the {@link Program}s as being used
|
|
||||||
*/
|
|
||||||
protected final void release(List<Loaded<Program>> loadedPrograms, Object consumer) {
|
|
||||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
|
||||||
loadedProgram.getDomainObject().release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyProcessorLabels(List<Option> options, Program program) {
|
private void applyProcessorLabels(List<Option> options, Program program) {
|
||||||
int id = program.startTransaction("Finalize load");
|
int id = program.startTransaction("Finalize load");
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -67,8 +67,8 @@ public abstract class AbstractProgramWrapperLoader extends AbstractProgramLoader
|
||||||
|
|
||||||
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
||||||
compilerSpec, consumer);
|
compilerSpec, consumer);
|
||||||
List<Loaded<Program>> loadedList =
|
List<Loaded<Program>> loadedList = List.of(
|
||||||
List.of(new Loaded<Program>(program, programName, programFolderPath));
|
new Loaded<Program>(program, programName, project, programFolderPath, consumer));
|
||||||
|
|
||||||
int transactionID = program.startTransaction("Loading");
|
int transactionID = program.startTransaction("Loading");
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -81,7 +81,7 @@ public abstract class AbstractProgramWrapperLoader extends AbstractProgramLoader
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(transactionID, true); // More efficient to commit when program will be discarded
|
program.endTransaction(transactionID, true); // More efficient to commit when program will be discarded
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedList, consumer);
|
loadedList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,7 +283,7 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
Program prog = createProgram(provider, programName, baseAddr, getName(), importerLanguage,
|
Program prog = createProgram(provider, programName, baseAddr, getName(), importerLanguage,
|
||||||
importerCompilerSpec, consumer);
|
importerCompilerSpec, consumer);
|
||||||
List<Loaded<Program>> loadedList =
|
List<Loaded<Program>> loadedList =
|
||||||
List.of(new Loaded<>(prog, programName, programFolderPath));
|
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
@ -294,7 +294,7 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedList, consumer);
|
loadedList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,11 +160,7 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
|
|
||||||
ProjectData projectData = project != null ? project.getProjectData() : null;
|
ProjectData projectData = project != null ? project.getProjectData() : null;
|
||||||
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(projectData, monitor)) {
|
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(projectData, monitor)) {
|
||||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
loadedPrograms.forEach(p -> esr.addProgramToFixup(p));
|
||||||
esr.addProgramToFixup(
|
|
||||||
loadedProgram.getProjectFolderPath() + loadedProgram.getName(),
|
|
||||||
loadedProgram.getDomainObject());
|
|
||||||
}
|
|
||||||
esr.fixUnresolvedExternalSymbols();
|
esr.fixUnresolvedExternalSymbols();
|
||||||
esr.logInfo(messageLog::appendMsg, true);
|
esr.logInfo(messageLog::appendMsg, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class GdtLoader implements Loader {
|
||||||
|
|
||||||
DataTypeArchive dtArchive =
|
DataTypeArchive dtArchive =
|
||||||
loadPackedProgramDatabase(provider, filename, consumer, monitor);
|
loadPackedProgramDatabase(provider, filename, consumer, monitor);
|
||||||
return new LoadResults<>(dtArchive, filename, projectFolderPath);
|
return new LoadResults<>(dtArchive, filename, project, projectFolderPath, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTypeArchive loadPackedProgramDatabase(ByteProvider provider, String programName,
|
private DataTypeArchive loadPackedProgramDatabase(ByteProvider provider, String programName,
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class GzfLoader implements Loader {
|
||||||
throws IOException, CancelledException, VersionException {
|
throws IOException, CancelledException, VersionException {
|
||||||
|
|
||||||
Program program = loadPackedProgramDatabase(provider, programName, consumer, monitor);
|
Program program = loadPackedProgramDatabase(provider, programName, consumer, monitor);
|
||||||
return new LoadResults<>(program, programName, projectFolderPath);
|
return new LoadResults<>(program, programName, project, projectFolderPath, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Program loadPackedProgramDatabase(ByteProvider provider, String programName,
|
private Program loadPackedProgramDatabase(ByteProvider provider, String programName,
|
||||||
|
|
|
@ -154,7 +154,7 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
||||||
Program prog = createProgram(provider, programName, null, getName(), importerLanguage,
|
Program prog = createProgram(provider, programName, null, getName(), importerLanguage,
|
||||||
importerCompilerSpec, consumer);
|
importerCompilerSpec, consumer);
|
||||||
List<Loaded<Program>> loadedList =
|
List<Loaded<Program>> loadedList =
|
||||||
List.of(new Loaded<>(prog, programName, programFolderPath));
|
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
loadInto(provider, loadSpec, options, log, prog, monitor);
|
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||||
|
@ -164,7 +164,7 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedList, consumer);
|
loadedList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.util.function.Predicate;
|
||||||
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
*
|
*
|
||||||
* @param <T> The type of {@link DomainObject}s that were loaded
|
* @param <T> The type of {@link DomainObject}s that were loaded
|
||||||
*/
|
*/
|
||||||
public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>> {
|
public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>, AutoCloseable {
|
||||||
|
|
||||||
private final List<Loaded<T>> loadedList;
|
private final List<Loaded<T>> loadedList;
|
||||||
|
|
||||||
|
@ -41,6 +40,8 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>
|
||||||
* Creates a new {@link LoadResults} that contains the given non-empty {@link List} of
|
* Creates a new {@link LoadResults} that contains the given non-empty {@link List} of
|
||||||
* {@link Loaded} {@link DomainObject}s. The first entry in the {@link List} is assumed to be
|
* {@link Loaded} {@link DomainObject}s. The first entry in the {@link List} is assumed to be
|
||||||
* the {@link #getPrimary() primary} {@link Loaded} {@link DomainObject}.
|
* the {@link #getPrimary() primary} {@link Loaded} {@link DomainObject}.
|
||||||
|
* <p>
|
||||||
|
* This object needs to be {@link #close() closed} when done with it.
|
||||||
*
|
*
|
||||||
* @param loadedList A {@link List} of {@link Loaded} {@link DomainObject}s
|
* @param loadedList A {@link List} of {@link Loaded} {@link DomainObject}s
|
||||||
* @throws IllegalArgumentException if the provided {@link List} is null or empty
|
* @throws IllegalArgumentException if the provided {@link List} is null or empty
|
||||||
|
@ -60,32 +61,77 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>
|
||||||
*
|
*
|
||||||
* @param domainObject The loaded {@link DomainObject}
|
* @param domainObject The loaded {@link DomainObject}
|
||||||
* @param name The name of the loaded {@link DomainObject}. If a
|
* @param name The name of the loaded {@link DomainObject}. If a
|
||||||
* {@link #save(Project, Object, MessageLog, TaskMonitor) save} occurs, this will attempted to
|
* {@link #save(TaskMonitor) save} occurs, this will attempted to be used for the resulting
|
||||||
* be used for the resulting {@link DomainFile}'s name.
|
* {@link DomainFile}'s name.
|
||||||
|
* @param project If not null, the project this will get saved to during a
|
||||||
|
* {@link #save(TaskMonitor)} operation
|
||||||
* @param projectFolderPath The project folder path this will get saved to during a
|
* @param projectFolderPath The project folder path this will get saved to during a
|
||||||
* {@link #save(Project, Object, MessageLog, TaskMonitor) save} operation. If null or empty,
|
* {@link #save(TaskMonitor) save} operation. If null or empty, the root project folder will
|
||||||
* the root project folder will be used.
|
* be used.
|
||||||
|
* @param consumer A reference to the object "consuming" the returned this
|
||||||
|
* {@link LoadResults}, used to ensure the underlying {@link DomainObject}s are only closed
|
||||||
|
* when every consumer is done with it (see {@link #close()}). NOTE: Wrapping a
|
||||||
|
* {@link DomainObject} in a {@link LoadResults} transfers responsibility of releasing the
|
||||||
|
* given {@link DomainObject} to this {@link LoadResults}'s {@link #close()} method.
|
||||||
*/
|
*/
|
||||||
public LoadResults(T domainObject, String name, String projectFolderPath) {
|
public LoadResults(T domainObject, String name, Project project, String projectFolderPath,
|
||||||
this(List.of(new Loaded<T>(domainObject, name, projectFolderPath)));
|
Object consumer) {
|
||||||
|
this(List.of(new Loaded<T>(domainObject, name, project, projectFolderPath, consumer)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the "primary" {@link Loaded} {@link DomainObject}, who's meaning is defined by each
|
* Gets the "primary" {@link Loaded} {@link DomainObject}, whose meaning is defined by each
|
||||||
* {@link Loader} implementation
|
* {@link Loader} implementation
|
||||||
*
|
*
|
||||||
* @return The "primary" {@link Loaded} {@link DomainObject}
|
* @return The "primary" {@link Loaded} {@link DomainObject}
|
||||||
*/
|
*/
|
||||||
public Loaded<T> getPrimary() {
|
public Loaded<T> getPrimary() {
|
||||||
return loadedList.get(0);
|
return loadedList.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the "primary" {@link DomainObject}, who's meaning is defined by each {@link Loader}
|
* Gets the "non-primary" {@link Loaded} {@link DomainObject}s, whose meaning is defined by each
|
||||||
* implementation
|
* {@link Loader} implementation
|
||||||
*
|
*
|
||||||
|
* @return The "non-primary" {@link Loaded} {@link DomainObject}s
|
||||||
|
*/
|
||||||
|
public List<Loaded<T>> getNonPrimary() {
|
||||||
|
return loadedList.stream().skip(1).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the "primary" {@link DomainObject}, whose meaning is defined by each {@link Loader}
|
||||||
|
* implementation.
|
||||||
|
* <p>
|
||||||
|
* NOTE: It is the responsibility of the caller to properly
|
||||||
|
* {@link DomainObject#release(Object) release} it when done. This
|
||||||
|
* {@link DomainObject#release(Object)} does not replace the requirement to
|
||||||
|
* {@link #close()} the {@link LoadResults} object when done.
|
||||||
|
*
|
||||||
|
* @param consumer A new reference to the object "consuming" the returned {@link DomainObject},
|
||||||
|
* used to ensure the underlying {@link DomainObject} is only released when every consumer is
|
||||||
|
* done with it (see {@link DomainObject#release(Object)}). NOTE: This method adds the given
|
||||||
|
* consumer to the returned {@link DomainObject}, requiring an explicit
|
||||||
|
* {@link DomainObject#release(Object)} to be called on the return value (this entire
|
||||||
|
* {@link LoadResults} must also still be {@link #close() closed}).
|
||||||
* @return The "primary" {@link DomainObject}
|
* @return The "primary" {@link DomainObject}
|
||||||
*/
|
*/
|
||||||
|
public T getPrimaryDomainObject(Object consumer) {
|
||||||
|
return loadedList.getFirst().getDomainObject(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the "primary" loaded {@link DomainObject}, whose meaning is defined by each
|
||||||
|
* {@link Loader} implementation. Unsafe resource management is used. Temporarily exists to
|
||||||
|
* provide backwards compatibility.
|
||||||
|
*
|
||||||
|
* @return The "primary" {@link DomainObject}
|
||||||
|
* @deprecated This class's internal {@link DomainObject}s are now cleaned up with the
|
||||||
|
* {@link #close()} method. If the primary {@link DomainObject} needs to be retrieved from
|
||||||
|
* this class, instead use {@link #getPrimaryDomainObject(Object)} and independently clean up
|
||||||
|
* the new reference with a separate call to {@link DomainObject#release(Object)}.
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public T getPrimaryDomainObject() {
|
public T getPrimaryDomainObject() {
|
||||||
return loadedList.get(0).getDomainObject();
|
return loadedList.get(0).getDomainObject();
|
||||||
}
|
}
|
||||||
|
@ -101,65 +147,44 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Loaded#save(Project, MessageLog, TaskMonitor) Saves} each {@link Loaded}
|
* {@link Loaded#save(TaskMonitor) Saves} each {@link Loaded} {@link DomainObject} to the given
|
||||||
* {@link DomainObject} to the given {@link Project}.
|
* {@link Project}.
|
||||||
* <p>
|
|
||||||
* NOTE: If any fail to save, none will be saved (already saved {@link DomainFile}s will be
|
|
||||||
* cleaned up/deleted), and all {@link Loaded} {@link DomainObject}s will have been
|
|
||||||
* {@link #release(Object) released}.
|
|
||||||
*
|
*
|
||||||
* @param project The {@link Project} to save to
|
|
||||||
* @param consumer the consumer
|
|
||||||
* @param messageLog The log
|
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
* @throws IOException If there was a problem saving
|
* @throws IOException If there was a problem saving. A thrown exception may result in only some
|
||||||
* @see Loaded#save(Project, MessageLog, TaskMonitor)
|
* of the {@link Loaded} elements being saved. It is the responsibility of the caller to clean
|
||||||
|
* things up appropriately.
|
||||||
|
* @see Loaded#save(TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public void save(Project project, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
public void save(TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
for (Loaded<T> loaded : loadedList) {
|
||||||
boolean success = false;
|
loaded.save(monitor);
|
||||||
try {
|
|
||||||
for (Loaded<T> loaded : loadedList) {
|
|
||||||
loaded.save(project, messageLog, monitor);
|
|
||||||
}
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (!success) {
|
|
||||||
for (Loaded<T> loaded : this) {
|
|
||||||
try {
|
|
||||||
loaded.release(consumer);
|
|
||||||
loaded.deleteSavedDomainFile(consumer);
|
|
||||||
}
|
|
||||||
catch (IOException e1) {
|
|
||||||
Msg.error(getClass(), "Failed to delete: " + loaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all of the {@link Loaded} {@link DomainObject}s that the specified consumer is no
|
* Unsafely notifies all of the {@link Loaded} {@link DomainObject}s that the specified consumer
|
||||||
* longer using them. When the last consumer invokes this method, the {@link Loaded}
|
* is no longer using them. Temporarily exists to provide backwards compatibility.
|
||||||
* {@link DomainObject}s will be closed and will become invalid.
|
|
||||||
*
|
*
|
||||||
* @param consumer the consumer
|
* @param consumer the consumer
|
||||||
|
* @deprecated Use {@link #close()} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public void release(Object consumer) {
|
public void release(Object consumer) {
|
||||||
loadedList.forEach(loaded -> loaded.release(consumer));
|
loadedList.forEach(loaded -> loaded.release(consumer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the filtered {@link Loaded} {@link DomainObject}s that the specified consumer is no
|
* Unsafely notifies the filtered {@link Loaded} {@link DomainObject}s that the specified
|
||||||
* longer using them. When the last consumer invokes this method, the filtered {@link Loaded}
|
* consumer is no longer using them. Temporarily exists to provide backwards compatibility.
|
||||||
* {@link DomainObject}s will be closed and will become invalid.
|
|
||||||
*
|
*
|
||||||
* @param consumer the consumer
|
* @param consumer the consumer
|
||||||
* @param filter a filter to apply to the {@link Loaded} {@link DomainObject}s prior to the
|
* @param filter a filter to apply to the {@link Loaded} {@link DomainObject}s prior to the
|
||||||
* release
|
* release
|
||||||
|
* @deprecated Use {@link #close()} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public void release(Object consumer, Predicate<? super Loaded<T>> filter) {
|
public void release(Object consumer, Predicate<? super Loaded<T>> filter) {
|
||||||
loadedList.stream().filter(filter).forEach(loaded -> loaded.release(consumer));
|
loadedList.stream().filter(filter).forEach(loaded -> loaded.release(consumer));
|
||||||
}
|
}
|
||||||
|
@ -170,7 +195,10 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>
|
||||||
* {@link DomainObject}s will be closed and will become invalid.
|
* {@link DomainObject}s will be closed and will become invalid.
|
||||||
*
|
*
|
||||||
* @param consumer the consumer
|
* @param consumer the consumer
|
||||||
|
* @deprecated Use {@link #getNonPrimary()} and {@link Loaded#close()} on the {@link List}
|
||||||
|
* elements instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public void releaseNonPrimary(Object consumer) {
|
public void releaseNonPrimary(Object consumer) {
|
||||||
for (int i = 0; i < loadedList.size(); i++) {
|
for (int i = 0; i < loadedList.size(); i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
|
@ -179,6 +207,18 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes this {@link LoadResults} and releases the reference on the object consuming it.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Any {@link DomainObject}s obtained via {@link #getPrimaryDomainObject(Object)} must
|
||||||
|
* still be explicitly {@link DomainObject#release(Object) released} after calling this method,
|
||||||
|
* since they were obtained with their own consumers.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
loadedList.forEach(Loaded::close);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Loaded<T>> iterator() {
|
public Iterator<Loaded<T>> iterator() {
|
||||||
return loadedList.iterator();
|
return loadedList.iterator();
|
||||||
|
|
|
@ -17,8 +17,9 @@ package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
|
@ -28,65 +29,116 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* A loaded {@link DomainObject} produced by a {@link Loader}. In addition to storing the loaded
|
* A loaded {@link DomainObject} produced by a {@link Loader}. In addition to storing the loaded
|
||||||
* {@link DomainObject}, it also stores the {@link Loader}'s desired name and project folder path
|
* {@link DomainObject}, it also stores the {@link Loader}'s desired name and project folder path
|
||||||
* for the loaded {@link DomainObject}, should it get saved to a project.
|
* for the loaded {@link DomainObject}, should it get saved to a project.
|
||||||
* <p>
|
|
||||||
* NOTE: If an object of this type is marked as {@link #setDiscard(boolean) discardable}, it should
|
|
||||||
* be {@link #release(Object) released} and not saved.
|
|
||||||
*
|
*
|
||||||
* @param <T> The type of {@link DomainObject} that was loaded
|
* @param <T> The type of {@link DomainObject} that was loaded
|
||||||
*/
|
*/
|
||||||
public class Loaded<T extends DomainObject> {
|
public class Loaded<T extends DomainObject> implements AutoCloseable {
|
||||||
|
|
||||||
private final T domainObject;
|
protected final T domainObject;
|
||||||
private final String name;
|
protected final String name;
|
||||||
private String projectFolderPath;
|
protected Project project;
|
||||||
|
protected String projectFolderPath;
|
||||||
|
protected Object loadedConsumer;
|
||||||
|
|
||||||
private DomainFile domainFile;
|
protected DomainFile domainFile;
|
||||||
private boolean ignoreSave;
|
|
||||||
private boolean discard;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link Loaded} object
|
* Creates a new {@link Loaded} object.
|
||||||
|
* <p>
|
||||||
|
* This object needs to be {@link #close() closed} when done with it.
|
||||||
*
|
*
|
||||||
* @param domainObject The loaded {@link DomainObject}
|
* @param domainObject The loaded {@link DomainObject}
|
||||||
* @param name The name of the loaded {@link DomainObject}. If a
|
* @param name The name of the loaded {@link DomainObject}. If a {@link #save(TaskMonitor)}
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor)} occurs, this will attempted to be used for
|
* occurs, this will attempted to be used for the resulting {@link DomainFile}'s name.
|
||||||
* the resulting {@link DomainFile}'s name.
|
* @param project If not null, the project this will get saved to during a
|
||||||
|
* {@link #save(TaskMonitor)} operation
|
||||||
* @param projectFolderPath The project folder path this will get saved to during a
|
* @param projectFolderPath The project folder path this will get saved to during a
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor)} operation. If null or empty, the root
|
* {@link #save(TaskMonitor)} operation. If null or empty, the root project folder will be
|
||||||
* project folder will be used.
|
* used.
|
||||||
|
* @param consumer A reference to the object "consuming" the returned {@link Loaded}
|
||||||
|
* {@link DomainObject}, used to ensure the underlying {@link DomainObject} is only closed
|
||||||
|
* when every consumer is done with it (see {@link #close()}). NOTE: Wrapping a
|
||||||
|
* {@link DomainObject} in a {@link Loaded} transfers responsibility of releasing the
|
||||||
|
* given {@link DomainObject} to this {@link Loaded}'s {@link #close()} method.
|
||||||
*/
|
*/
|
||||||
public Loaded(T domainObject, String name, String projectFolderPath) {
|
public Loaded(T domainObject, String name, Project project, String projectFolderPath,
|
||||||
|
Object consumer) {
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.project = project;
|
||||||
|
this.loadedConsumer = consumer;
|
||||||
setProjectFolderPath(projectFolderPath);
|
setProjectFolderPath(projectFolderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link Loaded} view on an existing {@link DomainFile}. This type of {@link Loaded}
|
* Gets the loaded {@link DomainObject}.
|
||||||
* object cannot be saved.
|
* <p>
|
||||||
|
* NOTE: The given It is the responsibility of the caller to properly
|
||||||
|
* {@link DomainObject#release(Object) release} it when done. This
|
||||||
|
* {@link DomainObject#release(Object)} does not replace the requirement to
|
||||||
|
* {@link #close()} the {@link Loaded} object when done.
|
||||||
*
|
*
|
||||||
* @param domainObject The loaded {@link DomainObject}
|
* @param consumer A new reference to the object "consuming" the returned {@link DomainObject},
|
||||||
* @param domainFile The {@link DomainFile} to be loaded
|
* used to ensure the underlying {@link DomainObject} is only released when every consumer is
|
||||||
|
* done with it (see {@link DomainObject#release(Object)}). NOTE: This method adds the given
|
||||||
|
* consumer to the returned {@link DomainObject}, requiring an explicit
|
||||||
|
* {@link DomainObject#release(Object)} to be called on the return value (this
|
||||||
|
* {@link Loaded} must also still be {@link #close() closed}).
|
||||||
|
* @return The loaded {@link DomainObject}
|
||||||
*/
|
*/
|
||||||
public Loaded(T domainObject, DomainFile domainFile) {
|
public T getDomainObject(Object consumer) {
|
||||||
this(domainObject, domainFile.getName(), domainFile.getParent().getPathname());
|
domainObject.addConsumer(consumer);
|
||||||
this.domainFile = domainFile;
|
return domainObject;
|
||||||
this.ignoreSave = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the loaded {@link DomainObject}
|
* Gets the loaded {@link DomainObject} with unsafe resource management. Temporarily exists
|
||||||
|
* to provide backwards compatibility.
|
||||||
*
|
*
|
||||||
* @return The loaded {@link DomainObject}
|
* @return The loaded {@link DomainObject}
|
||||||
|
* @deprecated This class's internal {@link DomainObject} is now cleaned up with the
|
||||||
|
* {@link #close()} method. If the {@link DomainObject} needs to be retrieved from this
|
||||||
|
* class, instead use {@link #getDomainObject(Object)} and independently clean up the new
|
||||||
|
* reference with a separate call to {@link DomainObject#release(Object)}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public T getDomainObject() {
|
public T getDomainObject() {
|
||||||
return domainObject;
|
return domainObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of the loaded {@link DomainObject}. If a
|
* Gets the loaded {@link DomainObject}'s type
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor)} occurs, this will attempted to be used for
|
*
|
||||||
* the resulting {@link DomainFile}'s name.
|
* @return the loaded {@link DomainObject}'s type
|
||||||
|
*/
|
||||||
|
public Class<? extends DomainObject> getDomainObjectType() {
|
||||||
|
return domainObject.getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely applies the given operation to the loaded {@link DomainObject} without the need to
|
||||||
|
* worry about resource management
|
||||||
|
*
|
||||||
|
* @param operation The operation to apply to the loaded {@link DomainObject}
|
||||||
|
*/
|
||||||
|
public void apply(Consumer<T> operation) {
|
||||||
|
operation.accept(domainObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely tests the given predicate on the loaded {@link DomainObject} without the need to
|
||||||
|
* worry about resource management
|
||||||
|
*
|
||||||
|
* @param predicate The predicate to test
|
||||||
|
* @return The result of the test
|
||||||
|
*/
|
||||||
|
public boolean check(Predicate<T> predicate) {
|
||||||
|
return predicate.test(domainObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the loaded {@link DomainObject}. If a {@link #save(TaskMonitor)} occurs,
|
||||||
|
* this will attempted to be used for the resulting {@link DomainFile}'s name.
|
||||||
*
|
*
|
||||||
* @return the name of the loaded {@link DomainObject}
|
* @return the name of the loaded {@link DomainObject}
|
||||||
*/
|
*/
|
||||||
|
@ -95,8 +147,18 @@ public class Loaded<T extends DomainObject> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the project folder path this will get saved to during a
|
* Gets the {@link Project} this will get saved to during a {@link #save(TaskMonitor)} operation
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor)} operation.
|
*
|
||||||
|
*@return The {@link Project} this will get saved to during a {@link #save(TaskMonitor)}
|
||||||
|
* operation (could be null)
|
||||||
|
*/
|
||||||
|
public Project getProject() {
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the project folder path this will get saved to during a {@link #save(TaskMonitor)}
|
||||||
|
* operation.
|
||||||
* <p>
|
* <p>
|
||||||
* NOTE: The returned path will always end with a "/".
|
* NOTE: The returned path will always end with a "/".
|
||||||
*
|
*
|
||||||
|
@ -107,12 +169,12 @@ public class Loaded<T extends DomainObject> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the project folder path this will get saved to during a
|
* Sets the project folder path this will get saved to during a {@link #save(TaskMonitor)}
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor)} operation.
|
* operation.
|
||||||
*
|
*
|
||||||
* @param projectFolderPath The project folder path this will get saved to during a
|
* @param projectFolderPath The project folder path this will get saved to during a
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor)} operation. If null or empty, the root
|
* {@link #save(TaskMonitor)} operation. If null or empty, the root project folder will be
|
||||||
* project folder will be used.
|
* used.
|
||||||
*/
|
*/
|
||||||
public void setProjectFolderPath(String projectFolderPath) {
|
public void setProjectFolderPath(String projectFolderPath) {
|
||||||
if (projectFolderPath == null || projectFolderPath.isBlank()) {
|
if (projectFolderPath == null || projectFolderPath.isBlank()) {
|
||||||
|
@ -124,19 +186,6 @@ public class Loaded<T extends DomainObject> {
|
||||||
this.projectFolderPath = projectFolderPath;
|
this.projectFolderPath = projectFolderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the loaded {@link DomainObject} that the specified consumer is no longer using it.
|
|
||||||
* When the last consumer invokes this method, the loaded {@link DomainObject} will be closed
|
|
||||||
* and will become invalid.
|
|
||||||
*
|
|
||||||
* @param consumer the consumer
|
|
||||||
*/
|
|
||||||
public void release(Object consumer) {
|
|
||||||
if (!domainObject.isClosed() && domainObject.isUsedBy(consumer)) {
|
|
||||||
domainObject.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the loaded {@link DomainObject} to the given {@link Project} at this object's
|
* Saves the loaded {@link DomainObject} to the given {@link Project} at this object's
|
||||||
* project folder path, using this object's name.
|
* project folder path, using this object's name.
|
||||||
|
@ -146,8 +195,6 @@ public class Loaded<T extends DomainObject> {
|
||||||
* Therefore, it should not be assumed that the returned {@link DomainFile} will have the same
|
* Therefore, it should not be assumed that the returned {@link DomainFile} will have the same
|
||||||
* name as a call to {@link #getName()}.
|
* name as a call to {@link #getName()}.
|
||||||
*
|
*
|
||||||
* @param project The {@link Project} to save to
|
|
||||||
* @param messageLog The log
|
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return The {@link DomainFile} where the save happened
|
* @return The {@link DomainFile} where the save happened
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
@ -155,13 +202,9 @@ public class Loaded<T extends DomainObject> {
|
||||||
* @throws IOException If there was an IO-related error, an invalid name was specified, or it
|
* @throws IOException If there was an IO-related error, an invalid name was specified, or it
|
||||||
* was already successfully saved and still exists
|
* was already successfully saved and still exists
|
||||||
*/
|
*/
|
||||||
public DomainFile save(Project project, MessageLog messageLog, TaskMonitor monitor)
|
public DomainFile save(TaskMonitor monitor)
|
||||||
throws CancelledException, ClosedException, IOException {
|
throws CancelledException, ClosedException, IOException {
|
||||||
|
|
||||||
if (ignoreSave) {
|
|
||||||
return domainFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (domainObject.isClosed()) {
|
if (domainObject.isClosed()) {
|
||||||
throw new ClosedException(
|
throw new ClosedException(
|
||||||
"Cannot saved closed DomainObject: " + domainObject.getName());
|
"Cannot saved closed DomainObject: " + domainObject.getName());
|
||||||
|
@ -202,13 +245,13 @@ public class Loaded<T extends DomainObject> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the loaded {@link DomainObject}'s associated {@link DomainFile} that was
|
* Gets the loaded {@link DomainObject}'s associated {@link DomainFile} that was
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor) saved}
|
* {@link #save(TaskMonitor) saved}
|
||||||
*
|
*
|
||||||
* @return The loaded {@link DomainObject}'s associated saved {@link DomainFile}, or null if
|
* @return The loaded {@link DomainObject}'s associated saved {@link DomainFile}, or null if
|
||||||
* was not saved
|
* was not saved
|
||||||
* @throws FileNotFoundException If the loaded {@link DomainObject} was saved but the associated
|
* @throws FileNotFoundException If the loaded {@link DomainObject} was saved but the associated
|
||||||
* {@link DomainFile} no longer exists
|
* {@link DomainFile} no longer exists
|
||||||
* @see #save(Project, MessageLog, TaskMonitor)
|
* @see #save(TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public DomainFile getSavedDomainFile() throws FileNotFoundException {
|
public DomainFile getSavedDomainFile() throws FileNotFoundException {
|
||||||
if (domainFile != null && !domainFile.exists()) {
|
if (domainFile != null && !domainFile.exists()) {
|
||||||
|
@ -218,42 +261,32 @@ public class Loaded<T extends DomainObject> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if this {@link Loaded} {@link DomainObject} should be discarded (not saved)
|
* Unsafely notifies the loaded {@link DomainObject} that the specified consumer is no longer
|
||||||
*
|
* using it. Temporarily exists to provide backwards compatibility.
|
||||||
* @return True if this {@link Loaded} {@link DomainObject} should be discarded; otherwise,
|
|
||||||
* false
|
|
||||||
*/
|
|
||||||
public boolean shouldDiscard() {
|
|
||||||
return discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether or not this {@link Loaded} {@link DomainObject} should be discarded (not saved)
|
|
||||||
*
|
|
||||||
* @param discard True if this {@link Loaded} {@link DomainObject} should be discarded;
|
|
||||||
* otherwise, false
|
|
||||||
*/
|
|
||||||
public void setDiscard(boolean discard) {
|
|
||||||
this.discard = discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the loaded {@link DomainObject}'s associated {@link DomainFile} that was
|
|
||||||
* {@link #save(Project, MessageLog, TaskMonitor) saved}. This method has no effect if it was
|
|
||||||
* never saved.
|
|
||||||
* <p>
|
|
||||||
* NOTE: The loaded {@link DomainObject} must be {@link #release(Object) released} prior to
|
|
||||||
* calling this method.
|
|
||||||
*
|
*
|
||||||
* @param consumer the consumer
|
* @param consumer the consumer
|
||||||
* @throws IOException If there was an issue deleting the saved {@link DomainFile}
|
* @deprecated Use {@link #close()} instead
|
||||||
* @see #save(Project, MessageLog, TaskMonitor)
|
|
||||||
*/
|
*/
|
||||||
void deleteSavedDomainFile(Object consumer) throws IOException {
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
if (domainFile != null && domainFile.exists()) {
|
public void release(Object consumer) {
|
||||||
domainFile.delete();
|
if (!domainObject.isClosed() && domainObject.isUsedBy(consumer)) {
|
||||||
domainFile = null;
|
domainObject.release(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes this {@link Loaded} {@link DomainObject} and releases the reference on the object
|
||||||
|
* consuming it.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Any {@link DomainObject}s obtained via {@link #getDomainObject(Object)} must still be
|
||||||
|
* explicitly {@link DomainObject#release(Object) released} after calling this method, since
|
||||||
|
* they were obtained with their own consumers.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (loadedConsumer != null && !domainObject.isClosed() &&
|
||||||
|
domainObject.isUsedBy(loadedConsumer)) {
|
||||||
|
domainObject.release(loadedConsumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A loaded, open {@link DomainObject} that has already been saved to a {@link DomainFile}
|
||||||
|
*
|
||||||
|
* @param <T> The type of open {@link DomainObject}
|
||||||
|
*/
|
||||||
|
public class LoadedOpen<T extends DomainObject> extends Loaded<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Loaded} view on an existing {@link DomainFile}. This type of {@link Loaded}
|
||||||
|
* object cannot be re-saved.
|
||||||
|
*
|
||||||
|
* @param domainObject The loaded {@link DomainObject}
|
||||||
|
* @param domainFile The {@link DomainFile} associated with the loaded {@link DomainObject}
|
||||||
|
* @param consumer A reference to the object "consuming" the returned {@link Loaded}
|
||||||
|
* {@link DomainObject}, used to ensure the underlying {@link DomainObject} is only closed
|
||||||
|
* when every consumer is done with it (see {@link #close()}). NOTE: Wrapping a
|
||||||
|
* {@link DomainObject} in a {@link Loaded} transfers responsibility of releasing the
|
||||||
|
* given {@link DomainObject} to this {@link Loaded}'s {@link #close()} method.
|
||||||
|
* @throws LoadException if the given {@link DomainFile} is not open
|
||||||
|
*/
|
||||||
|
public LoadedOpen(T domainObject, DomainFile domainFile, Object consumer) throws LoadException {
|
||||||
|
super(domainObject, domainFile.getName(), null, domainFile.getParent().getPathname(),
|
||||||
|
consumer);
|
||||||
|
this.domainFile = domainFile;
|
||||||
|
if (!domainFile.isOpen()) {
|
||||||
|
throw new LoadException(domainFile + " is not open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DomainFile save(TaskMonitor monitor) {
|
||||||
|
return domainFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -83,11 +83,10 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that when the load completes, the returned {@link Loaded} {@link DomainObject}s are not
|
* Note that when the load completes, the returned {@link Loaded} {@link DomainObject}s are not
|
||||||
* saved to a project. That is the responsibility of the caller (see
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
* {@link LoadResults#save(boolean, TaskMonitor)}).
|
||||||
* <p>
|
* <p>
|
||||||
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||||
* {@link DomainObject}s with {@link LoadResults#release(Object)} when they are no longer
|
* {@link DomainObject}s with {@link LoadResults#close()} when they are no longer needed.
|
||||||
* needed.
|
|
||||||
*
|
*
|
||||||
* @param provider The bytes to load.
|
* @param provider The bytes to load.
|
||||||
* @param loadedName A suggested name for the primary {@link Loaded} {@link DomainObject}.
|
* @param loadedName A suggested name for the primary {@link Loaded} {@link DomainObject}.
|
||||||
|
@ -105,7 +104,9 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
||||||
* @param loadSpec The {@link LoadSpec} to use during load.
|
* @param loadSpec The {@link LoadSpec} to use during load.
|
||||||
* @param options The load options.
|
* @param options The load options.
|
||||||
* @param messageLog The message log.
|
* @param messageLog The message log.
|
||||||
* @param consumer A consumer object for generated {@link DomainObject}s.
|
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||||
|
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||||
|
* with it (see {@link LoadResults#close()}).
|
||||||
* @param monitor A task monitor.
|
* @param monitor A task monitor.
|
||||||
* @return The {@link LoadResults} which contains one or more {@link Loaded}
|
* @return The {@link LoadResults} which contains one or more {@link Loaded}
|
||||||
* {@link DomainObject}s (created but not saved).
|
* {@link DomainObject}s (created but not saved).
|
||||||
|
|
|
@ -403,14 +403,20 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
List<DomainFolder> searchFolders =
|
List<DomainFolder> searchFolders =
|
||||||
getLibrarySearchFolders(loadedPrograms, project, options, log);
|
getLibrarySearchFolders(loadedPrograms, project, options, log);
|
||||||
|
|
||||||
List<LibrarySearchPath> searchPaths = getLibrarySearchPaths(
|
Program firstProgram = loadedPrograms.getFirst().getDomainObject(this);
|
||||||
loadedPrograms.getFirst().getDomainObject(), loadSpec, options, log, monitor);
|
List<LibrarySearchPath> searchPaths;
|
||||||
|
try {
|
||||||
|
searchPaths = getLibrarySearchPaths(firstProgram, loadSpec, options, log, monitor);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
firstProgram.release(this);
|
||||||
|
}
|
||||||
|
|
||||||
monitor.initialize(loadedPrograms.size());
|
monitor.initialize(loadedPrograms.size());
|
||||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||||
monitor.increment();
|
monitor.increment();
|
||||||
|
|
||||||
Program program = loadedProgram.getDomainObject();
|
Program program = loadedProgram.getDomainObject(this);
|
||||||
int id = program.startTransaction("Reexporting");
|
int id = program.startTransaction("Reexporting");
|
||||||
try {
|
try {
|
||||||
reexport(program, loadedPrograms, searchFolders, searchPaths, options, monitor,
|
reexport(program, loadedPrograms, searchFolders, searchPaths, options, monitor,
|
||||||
|
@ -421,6 +427,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true);
|
program.endTransaction(id, true);
|
||||||
|
program.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,12 +458,11 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
|
|
||||||
for (String path : getReexportPaths(program, log)) {
|
for (String path : getReexportPaths(program, log)) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
Program programToRelease = null;
|
Program lib = null;
|
||||||
try {
|
try {
|
||||||
Loaded<Program> match = findLibraryInLoadedList(loadedPrograms, path);
|
Loaded<Program> match = findLibraryInLoadedList(loadedPrograms, path);
|
||||||
Program lib = null;
|
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
lib = match.getDomainObject();
|
lib = match.getDomainObject(this);
|
||||||
}
|
}
|
||||||
if (lib == null) {
|
if (lib == null) {
|
||||||
for (DomainFolder searchFolder : searchFolders) {
|
for (DomainFolder searchFolder : searchFolders) {
|
||||||
|
@ -466,7 +472,9 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
DomainObject obj = df.getDomainObject(this, true, true, monitor);
|
DomainObject obj = df.getDomainObject(this, true, true, monitor);
|
||||||
if (obj instanceof Program p) {
|
if (obj instanceof Program p) {
|
||||||
lib = p;
|
lib = p;
|
||||||
programToRelease = p;
|
}
|
||||||
|
else {
|
||||||
|
obj.release(this);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -498,8 +506,8 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (programToRelease != null) {
|
if (lib != null) {
|
||||||
programToRelease.release(this);
|
lib.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
||||||
Program prog = createProgram(provider, programName, null, getName(), importerLanguage,
|
Program prog = createProgram(provider, programName, null, getName(), importerLanguage,
|
||||||
importerCompilerSpec, consumer);
|
importerCompilerSpec, consumer);
|
||||||
List<Loaded<Program>> loadedList =
|
List<Loaded<Program>> loadedList =
|
||||||
List.of(new Loaded<>(prog, programName, programFolderPath));
|
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
loadInto(provider, loadSpec, options, log, prog, monitor);
|
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||||
|
@ -182,7 +182,7 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedList, consumer);
|
loadedList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ public class MzLoader extends AbstractLibrarySupportLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTierPriority() {
|
public int getTierPriority() {
|
||||||
return 60; // we are less priority than PE! Important for AutoImporter
|
return 60; // we are less priority than PE! Important for ProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -200,7 +200,7 @@ public class XmlLoader extends AbstractProgramLoader {
|
||||||
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
||||||
importerCompilerSpec, consumer);
|
importerCompilerSpec, consumer);
|
||||||
List<Loaded<Program>> loadedList =
|
List<Loaded<Program>> loadedList =
|
||||||
List.of(new Loaded<>(prog, programName, programFolderPath));
|
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = doImport(result.lastXmlMgr, options, log, prog, monitor, false);
|
success = doImport(result.lastXmlMgr, options, log, prog, monitor, false);
|
||||||
|
@ -212,7 +212,7 @@ public class XmlLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedList, consumer);
|
loadedList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
import ghidra.app.util.importer.*;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.client.*;
|
import ghidra.framework.client.*;
|
||||||
|
@ -573,7 +573,7 @@ public class GhidraProject {
|
||||||
* @param program
|
* @param program
|
||||||
* the program on which the command is to be applied.
|
* the program on which the command is to be applied.
|
||||||
*/
|
*/
|
||||||
public void execute(Command cmd, Program program) {
|
public void execute(Command<Program> cmd, Program program) {
|
||||||
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
|
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
cmd.applyTo(program);
|
cmd.applyTo(program);
|
||||||
mgr.initializeOptions();
|
mgr.initializeOptions();
|
||||||
|
@ -593,67 +593,196 @@ public class GhidraProject {
|
||||||
openPrograms.put(program, id);
|
openPrograms.put(program, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program importProgram(File file, Language language,
|
/**
|
||||||
CompilerSpec compilerSpec) throws CancelledException, DuplicateNameException,
|
* Automatically imports the given {@link File} with the best matching {@link Loader} that
|
||||||
InvalidNameException, VersionException, IOException {
|
* supports the given language and compiler specification.
|
||||||
MessageLog messageLog = new MessageLog();
|
* <p>
|
||||||
LoadResults<Program> loadResults = AutoImporter.importByLookingForLcs(file, project, null,
|
* NOTE: It is the responsibility of the caller to release the returned {@link Program}
|
||||||
language, compilerSpec, this, messageLog, MONITOR);
|
* with {@link Program#release(Object)} when it is no longer needed, with {@code this}
|
||||||
Program program = loadResults.getPrimaryDomainObject();
|
* {@link GhidraProject} instance as the consumer.
|
||||||
loadResults.releaseNonPrimary(this);
|
*
|
||||||
initializeProgram(program, false);
|
* @param file The {@link File} to import
|
||||||
return program;
|
* @param language The desired {@link Language}
|
||||||
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
|
* @return The imported {@link Program}
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
|
public Program importProgram(File file, Language language, CompilerSpec compilerSpec)
|
||||||
|
throws CancelledException, VersionException, LanguageNotFoundException, LoadException,
|
||||||
|
IOException {
|
||||||
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.language(language)
|
||||||
|
.compiler(compilerSpec)
|
||||||
|
.monitor(MONITOR)
|
||||||
|
.load()) {
|
||||||
|
Program program = loadResults.getPrimaryDomainObject(this);
|
||||||
|
initializeProgram(program, false);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically imports the given {@link File} with the best matching {@link Loader} that
|
||||||
|
* supports the given processor.
|
||||||
|
* <p>
|
||||||
|
* NOTE: It is the responsibility of the caller to release the returned {@link Program}
|
||||||
|
* with {@link Program#release(Object)} when it is no longer needed, with {@code this}
|
||||||
|
* {@link GhidraProject} instance as the consumer.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param processor The desired {@link Processor}
|
||||||
|
* @return The imported {@link Program}
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public Program importProgram(File file, Processor processor) throws CancelledException,
|
public Program importProgram(File file, Processor processor) throws CancelledException,
|
||||||
DuplicateNameException, InvalidNameException, VersionException, IOException {
|
VersionException, LanguageNotFoundException, LoadException, IOException {
|
||||||
LanguageService svc = DefaultLanguageService.getLanguageService();
|
LanguageService svc = DefaultLanguageService.getLanguageService();
|
||||||
Language language = svc.getDefaultLanguage(processor);
|
Language language = svc.getDefaultLanguage(processor);
|
||||||
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
|
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
|
||||||
return importProgram(file, language, compilerSpec);
|
return importProgram(file, language, compilerSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically imports the given {@link File} with the given {@link Loader}.
|
||||||
|
* <p>
|
||||||
|
* NOTE: It is the responsibility of the caller to release the returned {@link Program}
|
||||||
|
* with {@link Program#release(Object)} when it is no longer needed, with {@code this}
|
||||||
|
* {@link GhidraProject} instance as the consumer.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param loaderClass The desired {@link Loader}
|
||||||
|
* @return The imported {@link Program}
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public Program importProgram(File file, Class<? extends Loader> loaderClass)
|
public Program importProgram(File file, Class<? extends Loader> loaderClass)
|
||||||
throws CancelledException, DuplicateNameException, InvalidNameException,
|
throws CancelledException, VersionException, LanguageNotFoundException, LoadException,
|
||||||
VersionException, IOException {
|
IOException {
|
||||||
MessageLog messageLog = new MessageLog();
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
LoadResults<Program> loadResults = AutoImporter.importByUsingSpecificLoaderClass(file,
|
.source(file)
|
||||||
project, null, loaderClass, null, this, messageLog, MONITOR);
|
.project(project)
|
||||||
Program program = loadResults.getPrimaryDomainObject();
|
.loaders(loaderClass)
|
||||||
loadResults.releaseNonPrimary(this);
|
.monitor(MONITOR)
|
||||||
initializeProgram(program, false);
|
.load()) {
|
||||||
return program;
|
Program program = loadResults.getPrimaryDomainObject(this);
|
||||||
|
initializeProgram(program, false);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically imports the given {@link File} with the given {@link Loader}, {@link Language},
|
||||||
|
* and {@link CompilerSpec compiler specification}.
|
||||||
|
* <p>
|
||||||
|
* NOTE: It is the responsibility of the caller to release the returned {@link Program}
|
||||||
|
* with {@link Program#release(Object)} when it is no longer needed, with {@code this}
|
||||||
|
* {@link GhidraProject} instance as the consumer.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param loaderClass The desired {@link Loader}
|
||||||
|
* @param language The desired {@link Language}
|
||||||
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
|
* @return The imported {@link Program}
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
public Program importProgram(File file, Class<? extends Loader> loaderClass, Language language,
|
public Program importProgram(File file, Class<? extends Loader> loaderClass, Language language,
|
||||||
CompilerSpec compilerSpec) throws CancelledException, DuplicateNameException,
|
CompilerSpec compilerSpec) throws CancelledException, VersionException,
|
||||||
InvalidNameException, VersionException, IOException {
|
LanguageNotFoundException, LoadException, IOException {
|
||||||
MessageLog messageLog = new MessageLog();
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, null);
|
.source(file)
|
||||||
LcsHintLoadSpecChooser opinionChoose = new LcsHintLoadSpecChooser(language, compilerSpec);
|
.project(project)
|
||||||
LoadResults<Program> loadResults =
|
.loaders(loaderClass)
|
||||||
AutoImporter.importFresh(file, project, null, this, messageLog, MONITOR, loaderFilter,
|
.language(language)
|
||||||
opinionChoose, null, new LoaderArgsOptionChooser(loaderFilter));
|
.compiler(compilerSpec)
|
||||||
loadResults.releaseNonPrimary(this);
|
.monitor(MONITOR)
|
||||||
return loadResults.getPrimaryDomainObject();
|
.load()) {
|
||||||
|
Program program = loadResults.getPrimaryDomainObject(this);
|
||||||
|
initializeProgram(program, false);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program importProgram(File file) throws CancelledException,
|
/**
|
||||||
DuplicateNameException, InvalidNameException, VersionException, IOException {
|
* Automatically imports the given {@link File}.
|
||||||
MessageLog messageLog = new MessageLog();
|
* <p>
|
||||||
LoadResults<Program> loadResults = AutoImporter.importByUsingBestGuess(file, project,
|
* NOTE: It is the responsibility of the caller to release the returned {@link Program}
|
||||||
null, this, messageLog, MONITOR);
|
* with {@link Program#release(Object)} when it is no longer needed, with {@code this}
|
||||||
Program program = loadResults.getPrimaryDomainObject();
|
* {@link GhidraProject} instance as the consumer.
|
||||||
loadResults.releaseNonPrimary(this);
|
*
|
||||||
initializeProgram(program, false);
|
* @param file The {@link File} to import
|
||||||
return program;
|
* @return The imported {@link Program}
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
|
public Program importProgram(File file) throws CancelledException, VersionException,
|
||||||
|
LanguageNotFoundException, LoadException, IOException {
|
||||||
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
|
.source(file)
|
||||||
|
.project(project)
|
||||||
|
.monitor(MONITOR)
|
||||||
|
.load()) {
|
||||||
|
Program program = loadResults.getPrimaryDomainObject(this);
|
||||||
|
initializeProgram(program, false);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program importProgramFast(File file) throws CancelledException, DuplicateNameException,
|
/**
|
||||||
InvalidNameException, VersionException, IOException {
|
* Automatically imports the given {@link File}.
|
||||||
Program program = importByStealingCodeFromAutoImporterByUsingBestGuess(file);
|
* <p>
|
||||||
initializeProgram(program, false);
|
* NOTE: It is the responsibility of the caller to release the returned {@link Program}
|
||||||
return program;
|
* with {@link Program#release(Object)} when it is no longer needed, with {@code this}
|
||||||
|
* {@link GhidraProject} instance as the consumer.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @return The imported {@link Program}
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws LanguageNotFoundException if there was a problem getting the language
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws VersionException if there was an issue with database versions, probably due to a
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if there was a problem loading
|
||||||
|
* @deprecated Use {@link ProgramLoader}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "11.5", forRemoval = true)
|
||||||
|
public Program importProgramFast(File file) throws CancelledException, VersionException,
|
||||||
|
LanguageNotFoundException, LoadException, IOException {
|
||||||
|
return importProgram(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -673,21 +802,6 @@ public class GhidraProject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Program importByStealingCodeFromAutoImporterByUsingBestGuess(File file)
|
|
||||||
throws CancelledException, DuplicateNameException, InvalidNameException,
|
|
||||||
VersionException, IOException {
|
|
||||||
|
|
||||||
MessageLog messageLog = new MessageLog();
|
|
||||||
|
|
||||||
String programNameOverride = null;
|
|
||||||
LoadResults<Program> loadResults =
|
|
||||||
AutoImporter.importFresh(file, project, null, this, messageLog, MONITOR,
|
|
||||||
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED,
|
|
||||||
programNameOverride, OptionChooser.DEFAULT_OPTIONS);
|
|
||||||
loadResults.releaseNonPrimary(this);
|
|
||||||
return loadResults.getPrimaryDomainObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -525,7 +525,7 @@ public class ImporterPlugin extends Plugin
|
||||||
Program program = manager.getCurrentProgram();
|
Program program = manager.getCurrentProgram();
|
||||||
|
|
||||||
TaskLauncher.launchModal("Show Load Libraries Dialog", monitor -> {
|
TaskLauncher.launchModal("Show Load Libraries Dialog", monitor -> {
|
||||||
ImporterUtilities.showLoadLibrariesDialog(program, tool, manager, monitor);
|
ImporterUtilities.showLoadLibrariesDialog(program, tool, monitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,8 +265,16 @@ public class ImporterUtilities {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showLoadLibrariesDialog(Program program, PluginTool tool,
|
/**
|
||||||
ProgramManager manager, TaskMonitor monitor) {
|
* Constructs a {@link LoadLibrariesOptionsDialog} and shows it in the swing thread. If the
|
||||||
|
* given {@link Program} does not support library loading, a warning message will be shown
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* @param program The {@link Program}
|
||||||
|
* @param tool The {@link PluginTool}
|
||||||
|
* @param monitor {@link TaskMonitor}
|
||||||
|
*/
|
||||||
|
public static void showLoadLibrariesDialog(Program program, PluginTool tool,TaskMonitor monitor) {
|
||||||
|
|
||||||
Objects.requireNonNull(monitor);
|
Objects.requireNonNull(monitor);
|
||||||
|
|
||||||
|
@ -279,9 +287,25 @@ public class ImporterUtilities {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ByteProvider provider = getProvider(program);
|
ByteProvider provider = getProvider(program);
|
||||||
|
if (provider == null) {
|
||||||
|
Msg.showWarn(null, null, LoadLibrariesOptionsDialog.TITLE,
|
||||||
|
"Cannot Load Libraries. Program does not have file bytes associated with it.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
LoadSpec loadSpec = getLoadSpec(provider, program);
|
LoadSpec loadSpec = getLoadSpec(provider, program);
|
||||||
|
if (loadSpec == null || loadSpec.getLoader()
|
||||||
|
.getDefaultOptions(provider, loadSpec, null, false)
|
||||||
|
.stream()
|
||||||
|
.noneMatch(e -> e.getName()
|
||||||
|
.equals(
|
||||||
|
AbstractLibrarySupportLoader.LOAD_ONLY_LIBRARIES_OPTION_NAME))) {
|
||||||
|
Msg.showWarn(null, null, LoadLibrariesOptionsDialog.TITLE,
|
||||||
|
"Cannot Load Libraries. Program does not support library loading.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
AddressFactory addressFactory =
|
AddressFactory addressFactory =
|
||||||
loadSpec.getLanguageCompilerSpec().getLanguage().getAddressFactory();
|
loadSpec.getLanguageCompilerSpec().getLanguage().getAddressFactory();
|
||||||
|
|
||||||
SystemUtilities.runSwingLater(() -> {
|
SystemUtilities.runSwingLater(() -> {
|
||||||
OptionsDialog dialog = new LoadLibrariesOptionsDialog(provider, program, tool,
|
OptionsDialog dialog = new LoadLibrariesOptionsDialog(provider, program, tool,
|
||||||
loadSpec, () -> addressFactory);
|
loadSpec, () -> addressFactory);
|
||||||
|
@ -345,20 +369,12 @@ public class ImporterUtilities {
|
||||||
Program program =
|
Program program =
|
||||||
doFSImportHelper((GFileSystemProgramProvider) refdFile.fsRef.getFilesystem(),
|
doFSImportHelper((GFileSystemProgramProvider) refdFile.fsRef.getFilesystem(),
|
||||||
gfile, destFolder, consumer, monitor);
|
gfile, destFolder, consumer, monitor);
|
||||||
if (program != null) {
|
if (program == null) {
|
||||||
LoadResults<? extends DomainObject> loadResults = new LoadResults<>(program,
|
return;
|
||||||
program.getName(), destFolder.getPathname());
|
}
|
||||||
boolean success = false;
|
try (LoadResults<? extends DomainObject> loadResults = new LoadResults<>(program,
|
||||||
try {
|
program.getName(), tool.getProject(), destFolder.getPathname(), consumer)) {
|
||||||
doPostImportProcessing(tool, programManager, fsrl, loadResults, consumer,
|
doPostImportProcessing(tool, programManager, fsrl, loadResults, "", monitor);
|
||||||
"", monitor);
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (!success) {
|
|
||||||
program.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
@ -420,14 +436,14 @@ public class ImporterUtilities {
|
||||||
|
|
||||||
Object consumer = new Object();
|
Object consumer = new Object();
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
try (LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
.load(bp, programName, tool.getProject(), destFolder.getPathname(), loadSpec,
|
.load(bp, programName, tool.getProject(), destFolder.getPathname(), loadSpec,
|
||||||
options, messageLog, consumer, monitor);
|
options, messageLog, consumer, monitor)) {
|
||||||
|
loadResults.save(monitor);
|
||||||
|
doPostImportProcessing(tool, programManager, fsrl, loadResults,
|
||||||
|
messageLog.toString(), monitor);
|
||||||
|
}
|
||||||
|
|
||||||
loadResults.save(tool.getProject(), consumer, messageLog, monitor);
|
|
||||||
|
|
||||||
doPostImportProcessing(tool, programManager, fsrl, loadResults, consumer,
|
|
||||||
messageLog.toString(), monitor);
|
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
// no need to show a message
|
// no need to show a message
|
||||||
|
@ -440,35 +456,39 @@ public class ImporterUtilities {
|
||||||
|
|
||||||
private static Set<DomainFile> doPostImportProcessing(PluginTool pluginTool,
|
private static Set<DomainFile> doPostImportProcessing(PluginTool pluginTool,
|
||||||
ProgramManager programManager, FSRL fsrl,
|
ProgramManager programManager, FSRL fsrl,
|
||||||
LoadResults<? extends DomainObject> loadResults, Object consumer, String importMessages,
|
LoadResults<? extends DomainObject> loadResults, String importMessages,
|
||||||
TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
boolean firstProgram = true;
|
boolean firstProgram = true;
|
||||||
Set<DomainFile> importedFilesSet = new HashSet<>();
|
Set<DomainFile> importedFilesSet = new HashSet<>();
|
||||||
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
|
Object consumer = new Object();
|
||||||
if (loaded.getDomainObject() instanceof Program program) {
|
DomainObject obj = loaded.getDomainObject(consumer);
|
||||||
if (programManager != null) {
|
try {
|
||||||
int openState = firstProgram
|
if (obj instanceof Program) {
|
||||||
? ProgramManager.OPEN_CURRENT
|
if (programManager != null) {
|
||||||
: ProgramManager.OPEN_VISIBLE;
|
int openState = firstProgram
|
||||||
programManager.openProgram(program, openState);
|
? ProgramManager.OPEN_CURRENT
|
||||||
|
: ProgramManager.OPEN_VISIBLE;
|
||||||
|
programManager.openProgram((Program) obj, openState);
|
||||||
|
}
|
||||||
|
importedFilesSet.add(obj.getDomainFile());
|
||||||
}
|
}
|
||||||
importedFilesSet.add(program.getDomainFile());
|
if (firstProgram) {
|
||||||
}
|
// currently we only show results for the imported program, not any libraries
|
||||||
if (firstProgram) {
|
displayResults(pluginTool, obj, obj.getDomainFile(), importMessages);
|
||||||
// currently we only show results for the imported program, not any libraries
|
|
||||||
displayResults(pluginTool, loaded.getDomainObject(),
|
|
||||||
loaded.getDomainObject().getDomainFile(), importMessages);
|
|
||||||
|
|
||||||
// Optionally echo loader message log to application.log
|
// Optionally echo loader message log to application.log
|
||||||
if (!Loader.loggingDisabled && !importMessages.isEmpty()) {
|
if (!Loader.loggingDisabled && !importMessages.isEmpty()) {
|
||||||
Msg.info(ImporterUtilities.class, "Additional info:\n" + importMessages);
|
Msg.info(ImporterUtilities.class, "Additional info:\n" + importMessages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
firstProgram = false;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
obj.release(consumer);
|
||||||
}
|
}
|
||||||
loaded.release(consumer);
|
|
||||||
firstProgram = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectFiles(importedFilesSet);
|
selectFiles(importedFilesSet);
|
||||||
|
|
|
@ -69,14 +69,14 @@ public class LoadLibrariesOptionsDialog extends OptionsDialog {
|
||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
TaskLauncher.launchNonModal(TITLE, monitor -> {
|
TaskLauncher.launchNonModal(TITLE, monitor -> {
|
||||||
super.okCallback();
|
super.okCallback();
|
||||||
try {
|
Object consumer = new Object();
|
||||||
Object consumer = new Object();
|
MessageLog messageLog = new MessageLog();
|
||||||
MessageLog messageLog = new MessageLog();
|
try (LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
|
||||||
.load(provider, program.getDomainFile().getName(), tool.getProject(),
|
.load(provider, program.getDomainFile().getName(), tool.getProject(),
|
||||||
program.getDomainFile().getParent().getPathname(), loadSpec,
|
program.getDomainFile().getParent().getPathname(), loadSpec,
|
||||||
getOptions(), messageLog, consumer, monitor);
|
getOptions(), messageLog, consumer, monitor)) {
|
||||||
loadResults.save(tool.getProject(), consumer, messageLog, monitor);
|
|
||||||
|
loadResults.save(monitor);
|
||||||
|
|
||||||
// Display results
|
// Display results
|
||||||
String importMessages = messageLog.toString();
|
String importMessages = messageLog.toString();
|
||||||
|
@ -90,8 +90,6 @@ public class LoadLibrariesOptionsDialog extends OptionsDialog {
|
||||||
else {
|
else {
|
||||||
Msg.showInfo(this, null, TITLE, "The program has no libraries.");
|
Msg.showInfo(this, null, TITLE, "The program has no libraries.");
|
||||||
}
|
}
|
||||||
|
|
||||||
loadResults.release(consumer);
|
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
// no need to show a message
|
// no need to show a message
|
||||||
|
|
|
@ -143,26 +143,22 @@ public class ImportBatchTask extends Task {
|
||||||
}
|
}
|
||||||
Pair<DomainFolder, String> destInfo = getDestinationInfo(batchLoadConfig, destFolder);
|
Pair<DomainFolder, String> destInfo = getDestinationInfo(batchLoadConfig, destFolder);
|
||||||
|
|
||||||
Object consumer = new Object();
|
|
||||||
try {
|
try {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
Project project = AppInfo.getActiveProject();
|
Project project = AppInfo.getActiveProject();
|
||||||
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
try (LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
.load(byteProvider, fixupProjectFilename(destInfo.second), project,
|
.load(byteProvider, fixupProjectFilename(destInfo.second), project,
|
||||||
destInfo.first.getPathname(), loadSpec,
|
destInfo.first.getPathname(), loadSpec,
|
||||||
getOptionsFor(batchLoadConfig, loadSpec, byteProvider), messageLog,
|
getOptionsFor(batchLoadConfig, loadSpec, byteProvider), messageLog,
|
||||||
consumer, monitor);
|
this, monitor)) {
|
||||||
|
|
||||||
// TODO: accumulate batch results
|
// TODO: accumulate batch results
|
||||||
if (loadResults != null) {
|
if (loadResults != null) {
|
||||||
try {
|
loadResults.save(monitor);
|
||||||
loadResults.save(project, consumer, messageLog, monitor);
|
|
||||||
processImportResults(loadResults, batchLoadConfig, monitor);
|
processImportResults(loadResults, batchLoadConfig, monitor);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
loadResults.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalAppsImported++;
|
totalAppsImported++;
|
||||||
|
|
||||||
Msg.info(this, "Imported " + destInfo.first + "/ " + destInfo.second + ", " +
|
Msg.info(this, "Imported " + destInfo.first + "/ " + destInfo.second + ", " +
|
||||||
|
@ -197,14 +193,20 @@ public class ImportBatchTask extends Task {
|
||||||
BatchLoadConfig appInfo, TaskMonitor monitor) {
|
BatchLoadConfig appInfo, TaskMonitor monitor) {
|
||||||
|
|
||||||
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
DomainObject obj = loaded.getDomainObject();
|
DomainObject obj = loaded.getDomainObject(this);
|
||||||
if (obj instanceof Program program) {
|
try {
|
||||||
if (programManager != null && totalObjsImported < MAX_PROGRAMS_TO_OPEN) {
|
if (obj instanceof Program program) {
|
||||||
programManager.openProgram(program,
|
if (programManager != null && totalObjsImported < MAX_PROGRAMS_TO_OPEN) {
|
||||||
totalObjsImported == 0 ? ProgramManager.OPEN_CURRENT
|
programManager.openProgram(program,
|
||||||
: ProgramManager.OPEN_VISIBLE);
|
totalObjsImported == 0 ? ProgramManager.OPEN_CURRENT
|
||||||
|
: ProgramManager.OPEN_VISIBLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
obj.release(this);
|
||||||
|
}
|
||||||
|
|
||||||
totalObjsImported++;
|
totalObjsImported++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ import ghidra.GhidraException;
|
||||||
import ghidra.GhidraTestApplicationLayout;
|
import ghidra.GhidraTestApplicationLayout;
|
||||||
import ghidra.app.plugin.core.analysis.EmbeddedMediaAnalyzer;
|
import ghidra.app.plugin.core.analysis.EmbeddedMediaAnalyzer;
|
||||||
import ghidra.app.util.bin.*;
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.opinion.LoadException;
|
import ghidra.app.util.opinion.LoadException;
|
||||||
import ghidra.app.util.opinion.LoadResults;
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.framework.*;
|
import ghidra.framework.*;
|
||||||
|
@ -81,19 +81,22 @@ public class ProgramExaminer {
|
||||||
private ProgramExaminer(ByteProvider provider) throws GhidraException {
|
private ProgramExaminer(ByteProvider provider) throws GhidraException {
|
||||||
initializeGhidra();
|
initializeGhidra();
|
||||||
messageLog = new MessageLog();
|
messageLog = new MessageLog();
|
||||||
LoadResults<Program> loadResults = null;
|
|
||||||
try {
|
try {
|
||||||
try {
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
loadResults = AutoImporter.importByUsingBestGuess(provider, null, null, this,
|
.source(provider)
|
||||||
messageLog, TaskMonitor.DUMMY);
|
.log(messageLog)
|
||||||
program = loadResults.getPrimaryDomainObject();
|
.load()) {
|
||||||
|
program = loadResults.getPrimaryDomainObject(this);
|
||||||
}
|
}
|
||||||
catch (LoadException e) {
|
catch (LoadException e) {
|
||||||
try {
|
try {
|
||||||
program = AutoImporter
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
.importAsBinary(provider, null, null, defaultLanguage, null, this,
|
.source(provider)
|
||||||
messageLog, TaskMonitor.DUMMY)
|
.language(defaultLanguage)
|
||||||
.getDomainObject();
|
.log(messageLog)
|
||||||
|
.load()) {
|
||||||
|
program = loadResults.getPrimaryDomainObject(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (LoadException e1) {
|
catch (LoadException e1) {
|
||||||
throw new GhidraException(
|
throw new GhidraException(
|
||||||
|
@ -106,9 +109,6 @@ public class ProgramExaminer {
|
||||||
throw new GhidraException(e);
|
throw new GhidraException(e);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (loadResults != null) {
|
|
||||||
loadResults.releaseNonPrimary(this);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import db.Transaction;
|
import db.Transaction;
|
||||||
|
import ghidra.app.util.opinion.Loaded;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
@ -68,40 +69,33 @@ public class ExternalSymbolResolver implements Closeable {
|
||||||
* is called.
|
* is called.
|
||||||
* <p>
|
* <p>
|
||||||
* The program should be fully persisted to the project if using this method, otherwise use
|
* The program should be fully persisted to the project if using this method, otherwise use
|
||||||
* {@link #addProgramToFixup(String, Program)} and specify the pathname the program will
|
* {@link #addProgramToFixup(Loaded)}.
|
||||||
* be saved to.
|
|
||||||
*
|
*
|
||||||
* @param program {@link Program} to fix
|
* @param program {@link Program} to fix
|
||||||
*/
|
*/
|
||||||
public void addProgramToFixup(Program program) {
|
public void addProgramToFixup(Program program) {
|
||||||
addProgramToFixup(program.getDomainFile().getPathname(), program);
|
String programPath = program.getDomainFile().getPathname();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues a program into this session that will be fixed when {@link #fixUnresolvedExternalSymbols()}
|
|
||||||
* is called.
|
|
||||||
*
|
|
||||||
* @param programPath string project path to the program
|
|
||||||
* @param program {@link Program} to fix
|
|
||||||
*/
|
|
||||||
public void addProgramToFixup(String programPath, Program program) {
|
|
||||||
programsToFix.add(new ProgramSymbolResolver(program, programPath));
|
programsToFix.add(new ProgramSymbolResolver(program, programPath));
|
||||||
addLoadedProgram(programPath, program);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an already opened program to this session, allowing it to be used as an external
|
|
||||||
* library without needing to look it up in the current project.
|
|
||||||
*
|
|
||||||
* @param programPath project path to already opened program
|
|
||||||
* @param program {@link Program}
|
|
||||||
*/
|
|
||||||
public void addLoadedProgram(String programPath, Program program) {
|
|
||||||
if (loadedPrograms.put(programPath, program) == null) {
|
if (loadedPrograms.put(programPath, program) == null) {
|
||||||
program.addConsumer(this);
|
program.addConsumer(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues a {@link Loaded} {@link Program} into this session that will be fixed when
|
||||||
|
* {@link #fixUnresolvedExternalSymbols()} is called.
|
||||||
|
*
|
||||||
|
* @param loaded The {@link Loaded} {@link Program} to fix
|
||||||
|
*/
|
||||||
|
public void addProgramToFixup(Loaded<Program> loaded) {
|
||||||
|
Program program = loaded.getDomainObject(this);
|
||||||
|
String programPath = loaded.getProjectFolderPath() + loaded.getName();
|
||||||
|
programsToFix.add(new ProgramSymbolResolver(program, programPath));
|
||||||
|
if (loadedPrograms.put(programPath, program) != null) {
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there was an error encountered when trying to open an external library.
|
* Returns true if there was an error encountered when trying to open an external library.
|
||||||
*
|
*
|
||||||
|
@ -122,7 +116,7 @@ public class ExternalSymbolResolver implements Closeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves any unresolved external symbols in each program that has been queued up via
|
* Resolves any unresolved external symbols in each program that has been queued up via
|
||||||
* {@link #addProgramToFixup(String, Program)}.
|
* {@link #addProgramToFixup(Loaded)} or {@link #addProgramToFixup(Program)}.
|
||||||
*
|
*
|
||||||
* @throws CancelledException if cancelled
|
* @throws CancelledException if cancelled
|
||||||
*/
|
*/
|
||||||
|
@ -152,7 +146,7 @@ public class ExternalSymbolResolver implements Closeable {
|
||||||
* released during {@link #close()} of this ExternalSymbolServer instance.
|
* released during {@link #close()} of this ExternalSymbolServer instance.
|
||||||
* <p>
|
* <p>
|
||||||
* This cache is shared between all ProgramSymbolResolver instances (that were created
|
* This cache is shared between all ProgramSymbolResolver instances (that were created
|
||||||
* by calling {@link #addProgramToFixup(String, Program)}).
|
* by calling {@link #addProgramToFixup(Loaded)} or {@link #addProgramToFixup(Program)}).
|
||||||
*
|
*
|
||||||
* @param libPath project path to a library program
|
* @param libPath project path to a library program
|
||||||
* @return {@link Program}, or null if not found or other error during opening
|
* @return {@link Program}, or null if not found or other error during opening
|
||||||
|
|
|
@ -103,13 +103,13 @@ public class ApkLoader extends DexLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(allLoadedPrograms, consumer);
|
allLoadedPrograms.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allLoadedPrograms.isEmpty()) {
|
if (allLoadedPrograms.isEmpty()) {
|
||||||
throw new LoadException("Operation finished with no programs to load");
|
throw new LoadException("Operation finished with no programs to load");
|
||||||
}
|
}
|
||||||
link(allLoadedPrograms.stream().map(e -> e.getDomainObject()).toList(), log, monitor);
|
link(allLoadedPrograms, log, monitor);
|
||||||
return allLoadedPrograms;
|
return allLoadedPrograms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,11 +300,9 @@ public class ApkLoader extends DexLoader {
|
||||||
* @param log the message log
|
* @param log the message log
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
*/
|
*/
|
||||||
private void link(List<Program> programList, MessageLog log, TaskMonitor monitor) {
|
private void link(List<Loaded<Program>> programList, MessageLog log, TaskMonitor monitor) {
|
||||||
MultiDexLinker linker = new MultiDexLinker(programList);
|
try (MultiDexLinker linker = new MultiDexLinker(programList)) {
|
||||||
try {
|
|
||||||
linker.link(monitor);
|
linker.link(monitor);
|
||||||
linker.clear(monitor);
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.appendException(e);
|
log.appendException(e);
|
||||||
|
|
|
@ -16,33 +16,20 @@
|
||||||
package ghidra.file.formats.android.multidex;
|
package ghidra.file.formats.android.multidex;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
|
import ghidra.app.util.opinion.Loaded;
|
||||||
import ghidra.file.formats.android.dex.DexHeaderFactory;
|
import ghidra.file.formats.android.dex.DexHeaderFactory;
|
||||||
import ghidra.file.formats.android.dex.format.DexHeader;
|
import ghidra.file.formats.android.dex.format.DexHeader;
|
||||||
import ghidra.file.formats.android.dex.format.MethodIDItem;
|
import ghidra.file.formats.android.dex.format.MethodIDItem;
|
||||||
import ghidra.file.formats.android.dex.util.DexUtil;
|
import ghidra.file.formats.android.dex.util.DexUtil;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.listing.DataIterator;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.symbol.ExternalLocation;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.model.symbol.ExternalManager;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.program.model.symbol.ExternalReference;
|
|
||||||
import ghidra.program.model.symbol.RefType;
|
|
||||||
import ghidra.program.model.symbol.Reference;
|
|
||||||
import ghidra.program.model.symbol.ReferenceManager;
|
|
||||||
import ghidra.program.model.symbol.SourceType;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
|
||||||
import ghidra.util.exception.InvalidInputException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +37,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* via the "method_lookup" section using
|
* via the "method_lookup" section using
|
||||||
* external references.
|
* external references.
|
||||||
*/
|
*/
|
||||||
public final class MultiDexLinker {
|
public final class MultiDexLinker implements AutoCloseable {
|
||||||
|
|
||||||
private List<Program> programs;
|
private List<Program> programs;
|
||||||
private Map<Program, DexHeader> dexMap = new HashMap<>();
|
private Map<Program, DexHeader> dexMap = new HashMap<>();
|
||||||
|
@ -109,8 +96,11 @@ public final class MultiDexLinker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiDexLinker(List<Program> programs) {
|
public MultiDexLinker(List<Loaded<Program>> loadedPrograms) {
|
||||||
this.programs = new ArrayList<>(programs);//create copy of list
|
this.programs = new ArrayList<>();
|
||||||
|
for (Loaded<Program> loaded : loadedPrograms) {
|
||||||
|
this.programs.add(loaded.getDomainObject(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void link(TaskMonitor monitor) throws CancelledException, IOException,
|
public void link(TaskMonitor monitor) throws CancelledException, IOException,
|
||||||
|
@ -121,12 +111,12 @@ public final class MultiDexLinker {
|
||||||
linkPrograms(monitor);
|
linkPrograms(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear(TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
Objects.requireNonNull(monitor);
|
public void close() {
|
||||||
|
programs.forEach(p -> p.release(this));
|
||||||
programs.clear();
|
programs.clear();
|
||||||
dexMap.clear();
|
dexMap.clear();
|
||||||
for (DexHeader header : cmpMap.keySet()) {
|
for (DexHeader header : cmpMap.keySet()) {
|
||||||
monitor.checkCancelled();
|
|
||||||
cmpMap.get(header).clear();
|
cmpMap.get(header).clear();
|
||||||
}
|
}
|
||||||
cmpMap.clear();
|
cmpMap.clear();
|
||||||
|
|
|
@ -19,17 +19,16 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.util.bin.*;
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.bin.format.coff.*;
|
import ghidra.app.util.bin.format.coff.*;
|
||||||
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
|
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
|
||||||
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
|
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
|
||||||
import ghidra.app.util.importer.*;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.DomainFolder;
|
||||||
import ghidra.framework.model.DomainObject;
|
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
import ghidra.program.model.lang.LanguageDescription;
|
import ghidra.program.model.lang.LanguageDescription;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -39,8 +38,6 @@ import ghidra.util.task.CancelOnlyWrappingTaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class ImportMSLibs extends GhidraScript {
|
public class ImportMSLibs extends GhidraScript {
|
||||||
final static Predicate<Loader> LOADER_FILTER = new SingleLoaderFilter(MSCoffLoader.class);
|
|
||||||
final static LoadSpecChooser LOADSPEC_CHOOSER = new CsHintLoadSpecChooser("windows");
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Exception {
|
protected void run() throws Exception {
|
||||||
|
@ -103,32 +100,32 @@ public class ImportMSLibs extends GhidraScript {
|
||||||
if (CoffMachineType.isMachineTypeDefined(header.getMagic())) {
|
if (CoffMachineType.isMachineTypeDefined(header.getMagic())) {
|
||||||
String[] splits = splitPreferredName(preferredName);
|
String[] splits = splitPreferredName(preferredName);
|
||||||
|
|
||||||
LoadResults<? extends DomainObject> loadResults =
|
try (LoadResults<Program> loadResults =
|
||||||
AutoImporter.importFresh(
|
ProgramLoader.builder()
|
||||||
coffProvider,
|
.source(coffProvider)
|
||||||
state.getProject(),
|
.project(state.getProject())
|
||||||
root.getPathname(),
|
.projectFolderPath(root.getPathname())
|
||||||
this,
|
.loaders(MSCoffLoader.class)
|
||||||
log,
|
.compiler("windows")
|
||||||
new CancelOnlyWrappingTaskMonitor(monitor),
|
.name(
|
||||||
LOADER_FILTER,
|
mangleNameBecauseDomainFoldersAreSoRetro(
|
||||||
LOADSPEC_CHOOSER,
|
splits[splits.length - 1]))
|
||||||
mangleNameBecauseDomainFoldersAreSoRetro(splits[splits.length - 1]),
|
.log(log)
|
||||||
OptionChooser.DEFAULT_OPTIONS);
|
.monitor(new CancelOnlyWrappingTaskMonitor(monitor))
|
||||||
|
.load()) {
|
||||||
try {
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
Program program = loaded.getDomainObject(this);
|
||||||
if (loaded.getDomainObject() instanceof Program program) {
|
try {
|
||||||
loaded.save(state.getProject(), log, monitor);
|
loaded.save(monitor);
|
||||||
DomainFolder destination =
|
DomainFolder destination =
|
||||||
establishFolder(root, file, program, isDebug, splits);
|
establishFolder(root, file, program, isDebug, splits);
|
||||||
program.getDomainFile().moveTo(destination);
|
program.getDomainFile().moveTo(destination);
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
loadResults.release(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (LoadException e) {
|
catch (LoadException e) {
|
||||||
|
|
|
@ -53,7 +53,6 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
|
@ -63,19 +62,19 @@ import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.bin.format.coff.*;
|
import ghidra.app.util.bin.format.coff.*;
|
||||||
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
|
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
|
||||||
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
|
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
|
||||||
import ghidra.app.util.importer.*;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import utilities.util.FileUtilities;
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
public class MSLibBatchImportWorker extends GhidraScript {
|
public class MSLibBatchImportWorker extends GhidraScript {
|
||||||
final static Predicate<Loader> LOADER_FILTER = new SingleLoaderFilter(MSCoffLoader.class);
|
|
||||||
final static LoadSpecChooser LOADSPEC_CHOOSER = new CsHintLoadSpecChooser("windows");
|
|
||||||
|
|
||||||
private static String getProcessId(String fallback) {
|
private static String getProcessId(String fallback) {
|
||||||
// something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
|
// something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
|
||||||
|
@ -173,8 +172,7 @@ public class MSLibBatchImportWorker extends GhidraScript {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importLibrary(DomainFolder currentLibraryFolder, File file, MessageLog log)
|
private void importLibrary(DomainFolder currentLibraryFolder, File file, MessageLog log)
|
||||||
throws CancelledException, DuplicateNameException, InvalidNameException,
|
throws CancelledException, InvalidNameException, VersionException, IOException {
|
||||||
VersionException, IOException {
|
|
||||||
try (RandomAccessByteProvider provider = new RandomAccessByteProvider(file)) {
|
try (RandomAccessByteProvider provider = new RandomAccessByteProvider(file)) {
|
||||||
if (!CoffArchiveHeader.isMatch(provider)) {
|
if (!CoffArchiveHeader.isMatch(provider)) {
|
||||||
return;
|
return;
|
||||||
|
@ -196,30 +194,34 @@ public class MSLibBatchImportWorker extends GhidraScript {
|
||||||
|
|
||||||
Pair<DomainFolder, String> pair =
|
Pair<DomainFolder, String> pair =
|
||||||
getFolderAndUniqueFile(currentLibraryFolder, preferredName);
|
getFolderAndUniqueFile(currentLibraryFolder, preferredName);
|
||||||
LoadResults<? extends DomainObject> loadResults = null;
|
try (LoadResults<? extends DomainObject> loadResults =
|
||||||
try {
|
ProgramLoader.builder()
|
||||||
loadResults = AutoImporter.importFresh(coffProvider,
|
.source(coffProvider)
|
||||||
state.getProject(), pair.first.getPathname(), this, log,
|
.project(state.getProject())
|
||||||
monitor, LOADER_FILTER, LOADSPEC_CHOOSER, pair.second,
|
.projectFolderPath(pair.first.getPathname())
|
||||||
OptionChooser.DEFAULT_OPTIONS);
|
.loaders(MSCoffLoader.class)
|
||||||
|
.compiler("windows")
|
||||||
|
.name(pair.second)
|
||||||
|
.log(log)
|
||||||
|
.monitor(monitor)
|
||||||
|
.load()) {
|
||||||
|
|
||||||
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
if (loaded.getDomainObject() instanceof Program program) {
|
DomainObject obj = loaded.getDomainObject(this);
|
||||||
loaded.save(state.getProject(), log, monitor);
|
try {
|
||||||
println(
|
if (obj instanceof Program) {
|
||||||
"Imported " + program.getDomainFile().getPathname());
|
loaded.save(monitor);
|
||||||
DomainFile progFile = program.getDomainFile();
|
DomainFile progFile = obj.getDomainFile();
|
||||||
|
println("Imported " + progFile.getPathname());
|
||||||
if (!progFile.isVersioned()) {
|
if (!progFile.isVersioned()) {
|
||||||
progFile.addToVersionControl(initalCheckInComment,
|
progFile.addToVersionControl(initalCheckInComment,
|
||||||
false, monitor);
|
false, monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
finally {
|
||||||
}
|
obj.release(this);
|
||||||
finally {
|
}
|
||||||
if (loadResults != null) {
|
|
||||||
loadResults.release(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
|
@ -25,8 +24,10 @@ import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.bin.format.coff.*;
|
import ghidra.app.util.bin.format.coff.*;
|
||||||
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
|
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
|
||||||
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
|
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
|
||||||
import ghidra.app.util.importer.*;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
|
import ghidra.app.util.opinion.MSCoffLoader;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.DomainFolder;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
|
@ -35,8 +36,6 @@ import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class RecursiveRecursiveMSLibImport extends GhidraScript {
|
public class RecursiveRecursiveMSLibImport extends GhidraScript {
|
||||||
final static Predicate<Loader> LOADER_FILTER = new SingleLoaderFilter(MSCoffLoader.class);
|
|
||||||
final static LoadSpecChooser LOADSPEC_CHOOSER = new CsHintLoadSpecChooser("windows");
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Exception {
|
protected void run() throws Exception {
|
||||||
|
@ -152,18 +151,19 @@ public class RecursiveRecursiveMSLibImport extends GhidraScript {
|
||||||
Pair<DomainFolder, String> pair =
|
Pair<DomainFolder, String> pair =
|
||||||
establishProgramFolder(currentLibrary, preferredName);
|
establishProgramFolder(currentLibrary, preferredName);
|
||||||
|
|
||||||
LoadResults<? extends DomainObject> loadResults =
|
try (LoadResults<? extends DomainObject> loadResults =
|
||||||
AutoImporter.importFresh(coffProvider, state.getProject(),
|
ProgramLoader.builder()
|
||||||
pair.first.getPathname(), this, log, monitor, LOADER_FILTER,
|
.source(coffProvider)
|
||||||
LOADSPEC_CHOOSER,
|
.project(state.getProject())
|
||||||
mangleNameBecauseDomainFoldersAreSoRetro(pair.second),
|
.projectFolderPath(pair.first.getPathname())
|
||||||
OptionChooser.DEFAULT_OPTIONS);
|
.loaders(MSCoffLoader.class)
|
||||||
|
.compiler("windows")
|
||||||
try {
|
.name(
|
||||||
loadResults.save(state.getProject(), this, log, monitor);
|
mangleNameBecauseDomainFoldersAreSoRetro(pair.second))
|
||||||
}
|
.log(log)
|
||||||
finally {
|
.monitor(monitor)
|
||||||
loadResults.release(this);
|
.load()) {
|
||||||
|
loadResults.save(monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,7 @@ public class SarifLoader extends AbstractProgramLoader {
|
||||||
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
||||||
importerCompilerSpec, consumer);
|
importerCompilerSpec, consumer);
|
||||||
List<Loaded<Program>> loadedList =
|
List<Loaded<Program>> loadedList =
|
||||||
List.of(new Loaded<>(prog, programName, programFolderPath));
|
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = doImport(result.lastSarifMgr, options, log, prog, monitor, false);
|
success = doImport(result.lastSarifMgr, options, log, prog, monitor, false);
|
||||||
|
@ -206,7 +206,7 @@ public class SarifLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedList, consumer);
|
loadedList.forEach(Loaded::close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,7 @@ import java.io.File;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import db.Transaction;
|
import db.Transaction;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.app.util.opinion.LoadResults;
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.base.project.GhidraProject;
|
import ghidra.base.project.GhidraProject;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
|
@ -57,15 +56,19 @@ public class TutorialDebuggerMaintenance extends AbstractGhidraHeadedIntegration
|
||||||
@Test
|
@Test
|
||||||
public void testRecreateTermminesGzf() throws Throwable {
|
public void testRecreateTermminesGzf() throws Throwable {
|
||||||
File termmines = Application.getModuleDataFile("TestResources", "termmines").getFile(false);
|
File termmines = Application.getModuleDataFile("TestResources", "termmines").getFile(false);
|
||||||
LoadResults<Program> results = AutoImporter.importByUsingBestGuess(termmines,
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
env.getProject(), "/", this, new MessageLog(), CONSOLE);
|
.source(termmines)
|
||||||
program = results.getPrimaryDomainObject();
|
.project(env.getProject())
|
||||||
try (Transaction tx = program.openTransaction("Analyze")) {
|
.monitor(CONSOLE)
|
||||||
program.setExecutablePath("/tmp/termmines");
|
.load()) {
|
||||||
GhidraProject.analyze(program);
|
program = loadResults.getPrimaryDomainObject(this);
|
||||||
|
try (Transaction tx = program.openTransaction("Analyze")) {
|
||||||
|
program.setExecutablePath("/tmp/termmines");
|
||||||
|
GhidraProject.analyze(program);
|
||||||
|
}
|
||||||
|
File dest = new File(termmines.getParentFile(), "termmines.gzf");
|
||||||
|
dest.delete();
|
||||||
|
program.saveToPackedFile(dest, CONSOLE);
|
||||||
}
|
}
|
||||||
File dest = new File(termmines.getParentFile(), "termmines.gzf");
|
|
||||||
dest.delete();
|
|
||||||
program.saveToPackedFile(dest, CONSOLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidraclass.debugger.screenshot;
|
package ghidraclass.debugger.screenshot;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
@ -74,8 +74,7 @@ import ghidra.app.plugin.core.terminal.TerminalProvider;
|
||||||
import ghidra.app.script.GhidraState;
|
import ghidra.app.script.GhidraState;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.app.util.opinion.LoadResults;
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.async.AsyncTestUtils;
|
import ghidra.async.AsyncTestUtils;
|
||||||
import ghidra.debug.api.modules.ModuleMapProposal;
|
import ghidra.debug.api.modules.ModuleMapProposal;
|
||||||
|
@ -389,13 +388,15 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||||
|
|
||||||
protected Program importModule(TraceModule module) throws Throwable {
|
protected Program importModule(TraceModule module) throws Throwable {
|
||||||
Program prog = null;
|
Program prog = null;
|
||||||
try {
|
long snap = flatDbg.getCurrentSnap();
|
||||||
long snap = flatDbg.getCurrentSnap();
|
try (LoadResults<Program> result = ProgramLoader.builder()
|
||||||
MessageLog log = new MessageLog();
|
.source(new File(module.getName(snap)))
|
||||||
LoadResults<Program> result = AutoImporter.importByUsingBestGuess(
|
.project(env.getProject())
|
||||||
new File(module.getName(snap)), env.getProject(), "/", this, log, monitor);
|
.monitor(monitor)
|
||||||
result.save(env.getProject(), this, log, monitor);
|
.load()) {
|
||||||
prog = result.getPrimaryDomainObject();
|
|
||||||
|
result.save(monitor);
|
||||||
|
prog = result.getPrimaryDomainObject(this);
|
||||||
GhidraProgramUtilities.markProgramNotToAskToAnalyze(prog);
|
GhidraProgramUtilities.markProgramNotToAskToAnalyze(prog);
|
||||||
programManager.openProgram(prog);
|
programManager.openProgram(prog);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assume.*;
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -31,8 +30,7 @@ import ghidra.app.plugin.core.analysis.AnalysisBackgroundCommand;
|
||||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||||
import ghidra.app.services.TraceRmiLauncherService;
|
import ghidra.app.services.TraceRmiLauncherService;
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.app.util.opinion.LoadResults;
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.debug.api.ValStr;
|
import ghidra.debug.api.ValStr;
|
||||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||||
|
@ -80,9 +78,13 @@ public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebug
|
||||||
@Test
|
@Test
|
||||||
public void testGetClassName() throws Exception {
|
public void testGetClassName() throws Exception {
|
||||||
ResourceFile rf = Application.getModuleDataFile("TestResources", "HelloWorld.class");
|
ResourceFile rf = Application.getModuleDataFile("TestResources", "HelloWorld.class");
|
||||||
LoadResults<Program> results = AutoImporter.importByUsingBestGuess(rf.getFile(false),
|
try (LoadResults<Program> results = ProgramLoader.builder()
|
||||||
env.getProject(), "/", this, new MessageLog(), monitor);
|
.source(rf.getFile(false))
|
||||||
program = results.getPrimaryDomainObject();
|
.project(env.getProject())
|
||||||
|
.monitor(monitor)
|
||||||
|
.load()) {
|
||||||
|
program = results.getPrimaryDomainObject(this);
|
||||||
|
}
|
||||||
AutoAnalysisManager analyzer = AutoAnalysisManager.getAnalysisManager(program);
|
AutoAnalysisManager analyzer = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
analyzer.reAnalyzeAll(null);
|
analyzer.reAnalyzeAll(null);
|
||||||
Command<Program> cmd = new AnalysisBackgroundCommand(analyzer, false);
|
Command<Program> cmd = new AnalysisBackgroundCommand(analyzer, false);
|
||||||
|
|
|
@ -30,17 +30,16 @@ import generic.theme.GThemeDefaults.Colors;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||||
import ghidra.app.plugin.core.references.*;
|
import ghidra.app.plugin.core.references.*;
|
||||||
import ghidra.app.util.importer.*;
|
import ghidra.app.util.importer.ProgramLoader;
|
||||||
import ghidra.app.util.opinion.LoadResults;
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.app.util.opinion.LoaderService;
|
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
import ghidra.framework.model.Project;
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator {
|
public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
|
@ -301,20 +300,14 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importFile(File file) throws CancelledException, DuplicateNameException,
|
private void importFile(File file) throws CancelledException, VersionException, IOException {
|
||||||
InvalidNameException, VersionException, IOException {
|
|
||||||
String programNameOverride = null;
|
|
||||||
Project project = env.getProject();
|
Project project = env.getProject();
|
||||||
LoadResults<Program> loadResults = AutoImporter.importFresh(file, project,
|
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||||
project.getProjectData().getRootFolder().getPathname(), this, new MessageLog(),
|
.source(file)
|
||||||
TaskMonitor.DUMMY, LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED,
|
.project(project)
|
||||||
programNameOverride, OptionChooser.DEFAULT_OPTIONS);
|
.projectFolderPath(project.getProjectData().getRootFolder().getPathname())
|
||||||
|
.load()) {
|
||||||
try {
|
loadResults.getPrimary().save(TaskMonitor.DUMMY);
|
||||||
loadResults.getPrimary().save(project, new MessageLog(), TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
loadResults.release(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue